Add Reapply Automation with prior functionality and rework header buttons for performance.

This commit is contained in:
Ottermandias 2024-03-15 15:36:20 +01:00
parent 8dde1689f7
commit 05d261d4a3
11 changed files with 612 additions and 364 deletions

View file

@ -124,7 +124,7 @@ public partial class GlamourerIpc
if (_objects.TryGetValue(id, out var data))
foreach (var obj in data.Objects)
{
_autoDesignApplier.ReapplyAutomation(obj, state.Identifier, state);
_autoDesignApplier.ReapplyAutomation(obj, state.Identifier, state, true);
_stateManager.ReapplyState(obj, StateSource.IpcManual);
}
}

View file

@ -215,7 +215,7 @@ public sealed class AutoDesignApplier : IDisposable
_state.ReapplyState(actor, StateSource.Fixed);
}
public void ReapplyAutomation(Actor actor, ActorIdentifier identifier, ActorState state)
public void ReapplyAutomation(Actor actor, ActorIdentifier identifier, ActorState state, bool reset)
{
if (!_config.EnableAutoDesigns)
return;
@ -223,7 +223,8 @@ public sealed class AutoDesignApplier : IDisposable
if (!GetPlayerSet(identifier, out var set))
return;
_state.ResetState(state, StateSource.Game);
if (reset)
_state.ResetState(state, StateSource.Game);
Reduce(actor, state, set, false, false);
}

View file

@ -19,12 +19,13 @@ namespace Glamourer.Gui;
[Flags]
public enum QdbButtons
{
ApplyDesign = 0x01,
RevertAll = 0x02,
RevertAutomation = 0x04,
RevertAdvanced = 0x08,
RevertEquip = 0x10,
RevertCustomize = 0x20,
ApplyDesign = 0x01,
RevertAll = 0x02,
RevertAutomation = 0x04,
RevertAdvanced = 0x08,
RevertEquip = 0x10,
RevertCustomize = 0x20,
ReapplyAutomation = 0x40,
}
public sealed class DesignQuickBar : Window, IDisposable
@ -118,6 +119,7 @@ public sealed class DesignQuickBar : Window, IDisposable
DrawRevertCustomizeButton(buttonSize);
DrawRevertAdvancedCustomization(buttonSize);
DrawRevertAutomationButton(buttonSize);
DrawReapplyAutomationButton(buttonSize);
}
private ActorIdentifier _playerIdentifier;
@ -249,7 +251,47 @@ public sealed class DesignQuickBar : Window, IDisposable
foreach (var actor in data.Objects)
{
_autoDesignApplier.ReapplyAutomation(actor, id, state!);
_autoDesignApplier.ReapplyAutomation(actor, id, state!, true);
_stateManager.ReapplyState(actor, StateSource.Manual);
}
}
private void DrawReapplyAutomationButton(Vector2 buttonSize)
{
if (!_config.EnableAutoDesigns)
return;
if (!_config.QdbButtons.HasFlag(QdbButtons.ReapplyAutomation))
return;
var available = 0;
var tooltip = string.Empty;
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{
available |= 1;
tooltip = "Left-Click: Reapply the player character's current automation on top of their current state.";
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Reapply {_targetIdentifier}'s current automation on top of their current 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.Repeat, buttonSize, tooltip, available);
ImGui.SameLine();
if (!clicked)
return;
foreach (var actor in data.Objects)
{
_autoDesignApplier.ReapplyAutomation(actor, id, state!, false);
_stateManager.ReapplyState(actor, StateSource.Manual);
}
}
@ -385,8 +427,14 @@ public sealed class DesignQuickBar : Window, IDisposable
_numButtons = 0;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAll))
++_numButtons;
if (_config.EnableAutoDesigns && _config.QdbButtons.HasFlag(QdbButtons.RevertAutomation))
++_numButtons;
if (_config.EnableAutoDesigns)
{
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAutomation))
++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.ReapplyAutomation))
++_numButtons;
}
if ((_config.UseAdvancedParameters || _config.UseAdvancedDyes) && _config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced))
++_numButtons;
if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize))

View file

@ -55,12 +55,12 @@ public class MainWindow : Window, IDisposable
public readonly NpcTab Npcs;
public readonly MessagesTab Messages;
public TabType SelectTab = TabType.None;
public TabType SelectTab;
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar,
NpcTab npcs, MainWindowPosition position)
: base(GetLabel())
: base("GlamourerMainWindow")
{
pi.UiBuilder.DisableGposeUiHide = true;
SizeConstraints = new WindowSizeConstraints()
@ -102,6 +102,7 @@ public class MainWindow : Window, IDisposable
? Flags | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize
: Flags & ~(ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize);
_position.IsOpen = IsOpen;
WindowName = GetLabel();
}
public void Dispose()
@ -177,8 +178,12 @@ public class MainWindow : Window, IDisposable
IsOpen = true;
}
private static string GetLabel()
=> Glamourer.Version.Length == 0
? "Glamourer###GlamourerMainWindow"
: $"Glamourer v{Glamourer.Version}###GlamourerMainWindow";
private string GetLabel()
=> (Glamourer.Version.Length == 0, _config.Ephemeral.IncognitoMode) switch
{
(true, true) => "Glamourer (Incognito Mode)###GlamourerMainWindow",
(true, false) => "Glamourer###GlamourerMainWindow",
(false, false) => $"Glamourer v{Glamourer.Version}###GlamourerMainWindow",
(false, true) => $"Glamourer v{Glamourer.Version} (Incognito Mode)###GlamourerMainWindow",
};
}

View file

@ -21,22 +21,68 @@ using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Tabs.ActorTab;
public class ActorPanel(
ActorSelector _selector,
StateManager _stateManager,
CustomizationDrawer _customizationDrawer,
EquipmentDrawer _equipmentDrawer,
AutoDesignApplier _autoDesignApplier,
Configuration _config,
DesignConverter _converter,
ObjectManager _objects,
DesignManager _designManager,
ImportService _importService,
ICondition _conditions,
DictModelChara _modelChara,
CustomizeParameterDrawer _parameterDrawer,
AdvancedDyePopup _advancedDyes)
public class ActorPanel
{
private readonly ActorSelector _selector;
private readonly StateManager _stateManager;
private readonly CustomizationDrawer _customizationDrawer;
private readonly EquipmentDrawer _equipmentDrawer;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly Configuration _config;
private readonly DesignConverter _converter;
private readonly ObjectManager _objects;
private readonly DesignManager _designManager;
private readonly ImportService _importService;
private readonly ICondition _conditions;
private readonly DictModelChara _modelChara;
private readonly CustomizeParameterDrawer _parameterDrawer;
private readonly AdvancedDyePopup _advancedDyes;
private readonly HeaderDrawer.Button[] _leftButtons;
private readonly HeaderDrawer.Button[] _rightButtons;
public ActorPanel(ActorSelector selector,
StateManager stateManager,
CustomizationDrawer customizationDrawer,
EquipmentDrawer equipmentDrawer,
AutoDesignApplier autoDesignApplier,
Configuration config,
DesignConverter converter,
ObjectManager objects,
DesignManager designManager,
ImportService importService,
ICondition conditions,
DictModelChara modelChara,
CustomizeParameterDrawer parameterDrawer,
AdvancedDyePopup advancedDyes)
{
_selector = selector;
_stateManager = stateManager;
_customizationDrawer = customizationDrawer;
_equipmentDrawer = equipmentDrawer;
_autoDesignApplier = autoDesignApplier;
_config = config;
_converter = converter;
_objects = objects;
_designManager = designManager;
_importService = importService;
_conditions = conditions;
_modelChara = modelChara;
_parameterDrawer = parameterDrawer;
_advancedDyes = advancedDyes;
_leftButtons =
[
new SetFromClipboardButton(this),
new ExportToClipboardButton(this),
new SaveAsDesignButton(this),
];
_rightButtons =
[
new LockedButton(this),
new HeaderDrawer.IncognitoButton(_config.Ephemeral),
];
}
private ActorIdentifier _identifier;
private string _actorName = string.Empty;
private Actor _actor = Actor.Null;
@ -81,9 +127,7 @@ public class ActorPanel(
{
var textColor = !_identifier.IsValid ? ImGui.GetColorU32(ImGuiCol.Text) :
_data.Valid ? ColorId.ActorAvailable.Value() : ColorId.ActorUnavailable.Value();
HeaderDrawer.Draw(_actorName, textColor, ImGui.GetColorU32(ImGuiCol.FrameBg),
3, SetFromClipboardButton(), ExportToClipboardButton(), SaveAsDesignButton(), LockedButton(),
HeaderDrawer.Button.IncognitoButton(_selector.IncognitoMode, v => _selector.IncognitoMode = v));
HeaderDrawer.Draw(_actorName, textColor, ImGui.GetColorU32(ImGuiCol.FrameBg), _leftButtons, _rightButtons);
SaveDesignDrawPopup();
}
@ -269,59 +313,9 @@ public class ActorPanel(
_stateManager.TurnHuman(_state, StateSource.Manual);
}
private HeaderDrawer.Button SetFromClipboardButton()
=> new()
{
Description =
"Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations.",
Icon = FontAwesomeIcon.Clipboard,
OnClick = SetFromClipboard,
Visible = _state != null,
Disabled = _state?.IsLocked ?? true,
};
private HeaderDrawer.Button ExportToClipboardButton()
=> new()
{
Description =
"Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.",
Icon = FontAwesomeIcon.Copy,
OnClick = ExportToClipboard,
Visible = _state?.ModelData.ModelId == 0,
};
private HeaderDrawer.Button SaveAsDesignButton()
=> new()
{
Description =
"Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.",
Icon = FontAwesomeIcon.Save,
OnClick = SaveDesignOpen,
Visible = _state?.ModelData.ModelId == 0,
};
private HeaderDrawer.Button LockedButton()
=> new()
{
Description = "The current state of this actor is locked by external tools.",
Icon = FontAwesomeIcon.Lock,
OnClick = () => { },
Disabled = true,
Visible = _state?.IsLocked ?? false,
TextColor = ColorId.ActorUnavailable.Value(),
BorderColor = ColorId.ActorUnavailable.Value(),
};
private string _newName = string.Empty;
private DesignBase? _newDesign;
private void SaveDesignOpen()
{
ImGui.OpenPopup("Save as Design");
_newName = _state!.Identifier.ToName();
_newDesign = _converter.Convert(_state, ApplicationRules.FromModifiers(_state));
}
private void SaveDesignDrawPopup()
{
if (!ImGuiUtil.OpenNameField("Save as Design", ref _newName))
@ -333,37 +327,6 @@ public class ActorPanel(
_newName = string.Empty;
}
private void SetFromClipboard()
{
try
{
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToBool();
var text = ImGui.GetClipboardText();
var design = _converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data.");
_stateManager.ApplyDesign(_state!, design, ApplySettings.ManualWithLinks);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {_identifier}.",
$"Could not apply clipboard to design {_identifier.Incognito(null)}", NotificationType.Error, false);
}
}
private void ExportToClipboard()
{
try
{
var text = _converter.ShareBase64(_state!, ApplicationRules.FromModifiers(_state!));
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {_identifier} data to clipboard.",
$"Could not copy data from design {_identifier.Incognito(null)} to clipboard", NotificationType.Error);
}
}
private void RevertButtons()
{
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
@ -371,18 +334,29 @@ public class ActorPanel(
_stateManager.ResetState(_state!, StateSource.Manual);
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Reapply State", Vector2.Zero, "Try to reapply the configured state if something went wrong.",
_state!.IsLocked))
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero,
"Reapply the current automation state for the character on top of its current state..",
!_config.EnableAutoDesigns || _state!.IsLocked))
{
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false);
_stateManager.ReapplyState(_actor, StateSource.Manual);
}
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero,
if (ImGuiUtil.DrawDisabledButton("Revert to Automation", Vector2.Zero,
"Try to revert the character to the state it would have using automated designs.",
!_config.EnableAutoDesigns || _state!.IsLocked))
{
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!);
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true);
_stateManager.ReapplyState(_actor, StateSource.Manual);
}
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Reapply", Vector2.Zero,
"Try to reapply the configured state if something went wrong. Should generally not be necessary.",
_state!.IsLocked))
_stateManager.ReapplyState(_actor, StateSource.Manual);
}
private void DrawApplyToSelf()
@ -414,4 +388,107 @@ public class ActorPanel(
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
ApplySettings.Manual);
}
private sealed class SetFromClipboardButton(ActorPanel panel)
: HeaderDrawer.Button
{
protected override string Description
=> "Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Clipboard;
public override bool Visible
=> panel._state != null;
protected override bool Disabled
=> panel._state?.IsLocked ?? true;
protected override void OnClick()
{
try
{
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToBool();
var text = ImGui.GetClipboardText();
var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data.");
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {panel._identifier}.",
$"Could not apply clipboard to design {panel._identifier.Incognito(null)}", NotificationType.Error, false);
}
}
}
private sealed class ExportToClipboardButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Copy;
public override bool Visible
=> panel._state?.ModelData.ModelId == 0;
protected override void OnClick()
{
try
{
var text = panel._converter.ShareBase64(panel._state!, ApplicationRules.FromModifiers(panel._state!));
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._identifier} data to clipboard.",
$"Could not copy data from design {panel._identifier.Incognito(null)} to clipboard", NotificationType.Error);
}
}
}
private sealed class SaveAsDesignButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Save;
public override bool Visible
=> panel._state?.ModelData.ModelId == 0;
protected override void OnClick()
{
ImGui.OpenPopup("Save as Design");
panel._newName = panel._state!.Identifier.ToName();
panel._newDesign = panel._converter.Convert(panel._state, ApplicationRules.FromModifiers(panel._state));
}
}
private sealed class LockedButton(ActorPanel panel) : HeaderDrawer.Button
{
protected override string Description
=> "The current state of this actor is locked by external tools.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Lock;
public override bool Visible
=> panel._state?.IsLocked ?? false;
protected override bool Disabled
=> true;
protected override uint BorderColor
=> ColorId.ActorUnavailable.Value();
protected override uint TextColor
=> ColorId.ActorUnavailable.Value();
protected override void OnClick()
{ }
}
}

View file

@ -29,10 +29,10 @@ public class SetPanel(
Configuration _config,
RandomRestrictionDrawer _randomDrawer)
{
private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log);
private string? _tempName;
private int _dragIndex = -1;
private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log);
private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(_config.Ephemeral)];
private string? _tempName;
private int _dragIndex = -1;
private Action? _endAction;
@ -47,8 +47,7 @@ public class SetPanel(
}
private void DrawHeader()
=> HeaderDrawer.Draw(_selector.SelectionName, 0, ImGui.GetColorU32(ImGuiCol.FrameBg), 0,
HeaderDrawer.Button.IncognitoButton(_selector.IncognitoMode, v => _selector.IncognitoMode = v));
=> HeaderDrawer.Draw(_selector.SelectionName, 0, ImGui.GetColorU32(ImGuiCol.FrameBg), [], _rightButtons);
private void DrawPanel()
{

View file

@ -15,79 +15,79 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using System;
using static Glamourer.Gui.Tabs.HeaderDrawer;
namespace Glamourer.Gui.Tabs.DesignTab;
public class DesignPanel(
DesignFileSystemSelector _selector,
CustomizationDrawer _customizationDrawer,
DesignManager _manager,
ObjectManager _objects,
StateManager _state,
EquipmentDrawer _equipmentDrawer,
ModAssociationsTab _modAssociations,
Configuration _config,
DesignDetailTab _designDetails,
DesignConverter _converter,
ImportService _importService,
MultiDesignPanel _multiDesignPanel,
CustomizeParameterDrawer _parameterDrawer,
DesignLinkDrawer _designLinkDrawer,
MaterialDrawer _materials)
public class DesignPanel
{
private readonly FileDialogManager _fileDialog = new();
private readonly FileDialogManager _fileDialog = new();
private readonly DesignFileSystemSelector _selector;
private readonly CustomizationDrawer _customizationDrawer;
private readonly DesignManager _manager;
private readonly ObjectManager _objects;
private readonly StateManager _state;
private readonly EquipmentDrawer _equipmentDrawer;
private readonly ModAssociationsTab _modAssociations;
private readonly Configuration _config;
private readonly DesignDetailTab _designDetails;
private readonly ImportService _importService;
private readonly DesignConverter _converter;
private readonly MultiDesignPanel _multiDesignPanel;
private readonly CustomizeParameterDrawer _parameterDrawer;
private readonly DesignLinkDrawer _designLinkDrawer;
private readonly MaterialDrawer _materials;
private readonly Button[] _leftButtons;
private readonly Button[] _rightButtons;
private HeaderDrawer.Button LockButton()
=> _selector.Selected == null
? HeaderDrawer.Button.Invisible
: _selector.Selected.WriteProtected()
? new HeaderDrawer.Button
{
Description = "Make this design editable.",
Icon = FontAwesomeIcon.Lock,
OnClick = () => _manager.SetWriteProtection(_selector.Selected!, false),
}
: new HeaderDrawer.Button
{
Description = "Write-protect this design.",
Icon = FontAwesomeIcon.LockOpen,
OnClick = () => _manager.SetWriteProtection(_selector.Selected!, true),
};
private HeaderDrawer.Button SetFromClipboardButton()
=> new()
{
Description =
"Try to apply a design from your clipboard over this design.\nHold Control to only apply gear.\nHold Shift to only apply customizations.",
Icon = FontAwesomeIcon.Clipboard,
OnClick = SetFromClipboard,
Visible = _selector.Selected != null,
Disabled = _selector.Selected?.WriteProtected() ?? true,
};
private HeaderDrawer.Button UndoButton()
=> new()
{
Description = "Undo the last change if you accidentally overwrote your design with a different one.",
Icon = FontAwesomeIcon.Undo,
OnClick = UndoOverwrite,
Visible = _selector.Selected != null,
Disabled = !_manager.CanUndo(_selector.Selected),
};
private HeaderDrawer.Button ExportToClipboardButton()
=> new()
{
Description = "Copy the current design to your clipboard.",
Icon = FontAwesomeIcon.Copy,
OnClick = ExportToClipboard,
Visible = _selector.Selected != null,
};
public DesignPanel(DesignFileSystemSelector selector,
CustomizationDrawer customizationDrawer,
DesignManager manager,
ObjectManager objects,
StateManager state,
EquipmentDrawer equipmentDrawer,
ModAssociationsTab modAssociations,
Configuration config,
DesignDetailTab designDetails,
DesignConverter converter,
ImportService importService,
MultiDesignPanel multiDesignPanel,
CustomizeParameterDrawer parameterDrawer,
DesignLinkDrawer designLinkDrawer,
MaterialDrawer materials)
{
_selector = selector;
_customizationDrawer = customizationDrawer;
_manager = manager;
_objects = objects;
_state = state;
_equipmentDrawer = equipmentDrawer;
_modAssociations = modAssociations;
_config = config;
_designDetails = designDetails;
_importService = importService;
_converter = converter;
_multiDesignPanel = multiDesignPanel;
_parameterDrawer = parameterDrawer;
_designLinkDrawer = designLinkDrawer;
_materials = materials;
_leftButtons =
[
new SetFromClipboardButton(this),
new UndoButton(this),
new ExportToClipboardButton(this),
];
_rightButtons =
[
new LockButton(this),
new IncognitoButton(_config.Ephemeral),
];
}
private void DrawHeader()
=> HeaderDrawer.Draw(SelectionName, 0, ImGui.GetColorU32(ImGuiCol.FrameBg),
3, SetFromClipboardButton(), UndoButton(), ExportToClipboardButton(), LockButton(),
HeaderDrawer.Button.IncognitoButton(_selector.IncognitoMode, v => _selector.IncognitoMode = v));
=> HeaderDrawer.Draw(SelectionName, 0, ImGui.GetColorU32(ImGuiCol.FrameBg), _leftButtons, _rightButtons);
private string SelectionName
=> _selector.Selected == null ? "No Selection" : _selector.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
@ -397,49 +397,6 @@ public class DesignPanel(
DrawSaveToDat();
}
private void SetFromClipboard()
{
try
{
var text = ImGui.GetClipboardText();
var (applyEquip, applyCustomize) = UiHelpers.ConvertKeysToBool();
var design = _converter.FromBase64(text, applyCustomize, applyEquip, out _)
?? throw new Exception("The clipboard did not contain valid data.");
_manager.ApplyDesign(_selector.Selected!, design);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {_selector.Selected!.Name}.",
$"Could not apply clipboard to design {_selector.Selected!.Identifier}", NotificationType.Error, false);
}
}
private void UndoOverwrite()
{
try
{
_manager.UndoDesignChange(_selector.Selected!);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not undo last changes to {_selector.Selected!.Name}.", NotificationType.Error,
false);
}
}
private void ExportToClipboard()
{
try
{
var text = _converter.ShareBase64(_selector.Selected!);
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {_selector.Selected!.Name} data to clipboard.",
$"Could not copy data from design {_selector.Selected!.Identifier} to clipboard", NotificationType.Error, false);
}
}
private void DrawApplyToSelf()
{
@ -497,4 +454,111 @@ public class DesignPanel(
private static unsafe string GetUserPath()
=> Framework.Instance()->UserPath;
private sealed class LockButton(DesignPanel panel) : Button
{
public override bool Visible
=> panel._selector.Selected != null;
protected override string Description
=> panel._selector.Selected!.WriteProtected()
? "Make this design editable."
: "Write-protect this design.";
protected override FontAwesomeIcon Icon
=> panel._selector.Selected!.WriteProtected()
? FontAwesomeIcon.Lock
: FontAwesomeIcon.LockOpen;
protected override void OnClick()
=> panel._manager.SetWriteProtection(panel._selector.Selected!, !panel._selector.Selected!.WriteProtected());
}
private sealed class SetFromClipboardButton(DesignPanel panel) : Button
{
public override bool Visible
=> panel._selector.Selected != null;
protected override bool Disabled
=> panel._selector.Selected?.WriteProtected() ?? true;
protected override string Description
=> "Try to apply a design from your clipboard over this design.\nHold Control to only apply gear.\nHold Shift to only apply customizations.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Clipboard;
protected override void OnClick()
{
try
{
var text = ImGui.GetClipboardText();
var (applyEquip, applyCustomize) = UiHelpers.ConvertKeysToBool();
var design = panel._converter.FromBase64(text, applyCustomize, applyEquip, out _)
?? throw new Exception("The clipboard did not contain valid data.");
panel._manager.ApplyDesign(panel._selector.Selected!, design);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {panel._selector.Selected!.Name}.",
$"Could not apply clipboard to design {panel._selector.Selected!.Identifier}", NotificationType.Error, false);
}
}
}
private sealed class UndoButton(DesignPanel panel) : Button
{
public override bool Visible
=> panel._selector.Selected != null;
protected override bool Disabled
=> !panel._manager.CanUndo(panel._selector.Selected);
protected override string Description
=> "Undo the last change if you accidentally overwrote your design with a different one.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Undo;
protected override void OnClick()
{
try
{
panel._manager.UndoDesignChange(panel._selector.Selected!);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not undo last changes to {panel._selector.Selected!.Name}.",
NotificationType.Error,
false);
}
}
}
private sealed class ExportToClipboardButton(DesignPanel panel) : Button
{
public override bool Visible
=> panel._selector.Selected != null;
protected override string Description
=> "Copy the current design to your clipboard.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Copy;
protected override void OnClick()
{
try
{
var text = panel._converter.ShareBase64(panel._selector.Selected!);
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._selector.Selected!.Name} data to clipboard.",
$"Could not copy data from design {panel._selector.Selected!.Identifier} to clipboard", NotificationType.Error, false);
}
}
}
}

View file

@ -8,76 +8,77 @@ namespace Glamourer.Gui.Tabs;
public static class HeaderDrawer
{
public struct Button
public abstract class Button
{
public static readonly Button Invisible = new()
{
Visible = false,
Width = 0,
};
protected abstract void OnClick();
public Action? OnClick;
public string Description = string.Empty;
public float Width;
public uint BorderColor;
public uint TextColor;
public FontAwesomeIcon Icon;
public bool Disabled;
public bool Visible;
protected virtual string Description
=> string.Empty;
public Button()
{
Visible = true;
Width = ImGui.GetFrameHeightWithSpacing();
BorderColor = ColorId.HeaderButtons.Value();
TextColor = ColorId.HeaderButtons.Value();
Disabled = false;
}
protected virtual uint BorderColor
=> ColorId.HeaderButtons.Value();
public readonly void Draw()
protected virtual uint TextColor
=> ColorId.HeaderButtons.Value();
protected virtual FontAwesomeIcon Icon
=> FontAwesomeIcon.None;
protected virtual bool Disabled
=> false;
public virtual bool Visible
=> true;
public void Draw(float width)
{
if (!Visible)
return;
using var color = ImRaii.PushColor(ImGuiCol.Border, BorderColor)
.Push(ImGuiCol.Text, TextColor, TextColor != 0);
if (ImGuiUtil.DrawDisabledButton(Icon.ToIconString(), new Vector2(Width, ImGui.GetFrameHeight()), string.Empty, Disabled, true))
OnClick?.Invoke();
if (ImGuiUtil.DrawDisabledButton(Icon.ToIconString(), new Vector2(width, ImGui.GetFrameHeight()), string.Empty, Disabled, true))
OnClick();
color.Pop();
ImGuiUtil.HoverTooltip(Description);
}
public static Button IncognitoButton(bool current, Action<bool> setter)
=> current
? new Button
{
Description = "Toggle incognito mode off.",
Icon = FontAwesomeIcon.EyeSlash,
OnClick = () => setter(false),
}
: new Button
{
Description = "Toggle incognito mode on.",
Icon = FontAwesomeIcon.Eye,
OnClick = () => setter(true),
};
}
public static void Draw(string text, uint textColor, uint frameColor, int leftButtons, params Button[] buttons)
public sealed class IncognitoButton(EphemeralConfig config) : Button
{
protected override string Description
=> config.IncognitoMode
? "Toggle incognito mode off."
: "Toggle incognito mode on.";
protected override FontAwesomeIcon Icon
=> config.IncognitoMode
? FontAwesomeIcon.EyeSlash
: FontAwesomeIcon.Eye;
protected override void OnClick()
{
config.IncognitoMode = !config.IncognitoMode;
config.Save();
}
}
public static void Draw(string text, uint textColor, uint frameColor, Button[] leftButtons, Button[] rightButtons)
{
var width = ImGui.GetFrameHeightWithSpacing();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0)
.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
var leftButtonSize = 0f;
foreach (var button in buttons.Take(leftButtons).Where(b => b.Visible))
foreach (var button in leftButtons.Where(b => b.Visible))
{
button.Draw();
button.Draw(width);
ImGui.SameLine();
leftButtonSize += button.Width;
leftButtonSize += width;
}
var rightButtonSize = buttons.Length > leftButtons ? buttons.Skip(leftButtons).Where(b => b.Visible).Select(b => b.Width).Sum() : 0f;
var rightButtonSize = rightButtons.Count(b => b.Visible) * width;
var midSize = ImGui.GetContentRegionAvail().X - rightButtonSize - ImGuiHelpers.GlobalScale;
style.Pop();
@ -89,10 +90,10 @@ public static class HeaderDrawer
style.Pop();
style.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
foreach (var button in buttons.Skip(leftButtons).Where(b => b.Visible))
foreach (var button in rightButtons.Where(b => b.Visible))
{
ImGui.SameLine();
button.Draw();
button.Draw(width);
}
}
}

View file

@ -12,23 +12,57 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using static Glamourer.Gui.Tabs.HeaderDrawer;
namespace Glamourer.Gui.Tabs.NpcTab;
public class NpcPanel(
NpcSelector _selector,
LocalNpcAppearanceData _favorites,
CustomizationDrawer _customizeDrawer,
EquipmentDrawer _equipDrawer,
DesignConverter _converter,
DesignManager _designManager,
StateManager _state,
ObjectManager _objects,
DesignColors _colors)
public class NpcPanel
{
private readonly DesignColorCombo _colorCombo = new(_colors, true);
private string _newName = string.Empty;
private DesignBase? _newDesign;
private readonly DesignColorCombo _colorCombo;
private string _newName = string.Empty;
private DesignBase? _newDesign;
private readonly NpcSelector _selector;
private readonly LocalNpcAppearanceData _favorites;
private readonly CustomizationDrawer _customizeDrawer;
private readonly EquipmentDrawer _equipDrawer;
private readonly DesignConverter _converter;
private readonly DesignManager _designManager;
private readonly StateManager _state;
private readonly ObjectManager _objects;
private readonly DesignColors _colors;
private readonly Button[] _leftButtons;
private readonly Button[] _rightButtons;
public NpcPanel(NpcSelector selector,
LocalNpcAppearanceData favorites,
CustomizationDrawer customizeDrawer,
EquipmentDrawer equipDrawer,
DesignConverter converter,
DesignManager designManager,
StateManager state,
ObjectManager objects,
DesignColors colors)
{
_selector = selector;
_favorites = favorites;
_customizeDrawer = customizeDrawer;
_equipDrawer = equipDrawer;
_converter = converter;
_designManager = designManager;
_state = state;
_objects = objects;
_colors = colors;
_colorCombo = new DesignColorCombo(colors, true);
_leftButtons =
[
new ExportToClipboardButton(this),
new SaveAsDesignButton(this),
];
_rightButtons =
[
new FavoriteButton(this),
];
}
public void Draw()
{
@ -41,67 +75,30 @@ public class NpcPanel(
private void DrawHeader()
{
HeaderDrawer.Draw(_selector.HasSelection ? _selector.Selection.Name : "No Selection", ColorId.NormalDesign.Value(),
ImGui.GetColorU32(ImGuiCol.FrameBg), 2, ExportToClipboardButton(), SaveAsDesignButton(), FavoriteButton());
ImGui.GetColorU32(ImGuiCol.FrameBg), _leftButtons, _rightButtons);
SaveDesignDrawPopup();
}
private HeaderDrawer.Button FavoriteButton()
private sealed class FavoriteButton(NpcPanel panel) : Button
{
var (desc, color) = _favorites.IsFavorite(_selector.Selection)
? ("Remove this NPC appearance from your favorites.", ColorId.FavoriteStarOn.Value())
: ("Add this NPC Appearance to your favorites.", 0x80000000);
return new HeaderDrawer.Button
{
Icon = FontAwesomeIcon.Star,
OnClick = () => _favorites.ToggleFavorite(_selector.Selection),
Visible = _selector.HasSelection,
Description = desc,
TextColor = color,
};
}
protected override string Description
=> panel._favorites.IsFavorite(panel._selector.Selection)
? "Remove this NPC appearance from your favorites."
: "Add this NPC Appearance to your favorites.";
private HeaderDrawer.Button ExportToClipboardButton()
=> new()
{
Description =
"Copy the current NPCs appearance to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.",
Icon = FontAwesomeIcon.Copy,
OnClick = ExportToClipboard,
Visible = _selector.HasSelection,
};
protected override uint TextColor
=> panel._favorites.IsFavorite(panel._selector.Selection)
? ColorId.FavoriteStarOn.Value()
: 0x80000000;
private HeaderDrawer.Button SaveAsDesignButton()
=> new()
{
Description =
"Save this NPCs appearance as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.",
Icon = FontAwesomeIcon.Save,
OnClick = SaveDesignOpen,
Visible = _selector.HasSelection,
};
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Star;
private void ExportToClipboard()
{
try
{
var data = ToDesignData();
var text = _converter.ShareBase64(data, new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {_selector.Selection.Name}'s data to clipboard.",
$"Could not copy data from NPC appearance {_selector.Selection.Kind} {_selector.Selection.Id.Id} to clipboard",
NotificationType.Error);
}
}
public override bool Visible
=> panel._selector.HasSelection;
private void SaveDesignOpen()
{
ImGui.OpenPopup("Save as Design");
_newName = _selector.Selection.Name;
var data = ToDesignData();
_newDesign = _converter.Convert(data, new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
protected override void OnClick()
=> panel._favorites.ToggleFavorite(panel._selector.Selection);
}
private void SaveDesignDrawPopup()
@ -289,4 +286,52 @@ public class NpcPanel(
ImGuiUtil.HoverTooltip("Click to copy to clipboard.");
}
}
private sealed class ExportToClipboardButton(NpcPanel panel) : Button
{
protected override string Description
=> "Copy the current NPCs appearance to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Copy;
public override bool Visible
=> panel._selector.HasSelection;
protected override void OnClick()
{
try
{
var data = panel.ToDesignData();
var text = panel._converter.ShareBase64(data, new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
ImGui.SetClipboardText(text);
}
catch (Exception ex)
{
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._selector.Selection.Name}'s data to clipboard.",
$"Could not copy data from NPC appearance {panel._selector.Selection.Kind} {panel._selector.Selection.Id.Id} to clipboard",
NotificationType.Error);
}
}
}
private sealed class SaveAsDesignButton(NpcPanel panel) : Button
{
protected override string Description
=> "Save this NPCs appearance as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.";
protected override FontAwesomeIcon Icon
=> FontAwesomeIcon.Save;
public override bool Visible
=> panel._selector.HasSelection;
protected override void OnClick()
{
ImGui.OpenPopup("Save as Design");
panel._newName = panel._selector.Selection.Name;
var data = panel.ToDesignData();
panel._newDesign = panel._converter.Convert(data, new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
}
}
}

View file

@ -211,7 +211,7 @@ public class SettingsTab(
{
var showAuto = config.EnableAutoDesigns;
var showAdvanced = config.UseAdvancedParameters || config.UseAdvancedDyes;
var numColumns = 6 - (showAuto ? 0 : 1) - (showAdvanced ? 0 : 1);
var numColumns = 7 - (showAuto ? 0 : 2) - (showAdvanced ? 0 : 1);
ImGui.NewLine();
ImGui.TextUnformatted("Show the Following Buttons in the Quick Design Bar:");
ImGui.Dummy(Vector2.Zero);
@ -225,8 +225,9 @@ public class SettingsTab(
(" Apply Design ", true, QdbButtons.ApplyDesign),
(" Revert All ", true, QdbButtons.RevertAll),
(" Revert to Auto ", showAuto, QdbButtons.RevertAutomation),
(" Reapply Auto ", showAuto, QdbButtons.ReapplyAutomation),
(" Revert Equip ", true, QdbButtons.RevertEquip),
(" Revert Customization ", true, QdbButtons.RevertCustomize),
(" Revert Customize ", true, QdbButtons.RevertCustomize),
(" Revert Advanced ", showAdvanced, QdbButtons.RevertAdvanced),
};

View file

@ -115,16 +115,17 @@ public class CommandService : IDisposable
var argument = argumentList.Length == 2 ? argumentList[1] : string.Empty;
var _ = argumentList[0].ToLowerInvariant() switch
{
"apply" => Apply(argument),
"reapply" => ReapplyState(argument),
"revert" => Revert(argument),
"reapplyautomation" => ReapplyAutomation(argument),
"automation" => SetAutomation(argument),
"copy" => CopyState(argument),
"save" => SaveState(argument),
"delete" => Delete(argument),
"applyitem" => ApplyItem(argument),
_ => PrintHelp(argumentList[0]),
"apply" => Apply(argument),
"reapply" => ReapplyState(argument),
"revert" => Revert(argument),
"reapplyautomation" => ReapplyAutomation(argument, "reapplyautomation", false),
"reverttoautomation" => ReapplyAutomation(argument, "reverttoautomation", true),
"automation" => SetAutomation(argument),
"copy" => CopyState(argument),
"save" => SaveState(argument),
"delete" => Delete(argument),
"applyitem" => ApplyItem(argument),
_ => PrintHelp(argumentList[0]),
};
}
@ -143,6 +144,8 @@ public class CommandService : IDisposable
_chat.Print(new SeStringBuilder().AddCommand("revert", "Reverts a given character to its game state. Use without arguments for help.")
.BuiltString);
_chat.Print(new SeStringBuilder().AddCommand("reapplyautomation",
"Reapplies the current automation state on top of the characters current state.. Use without arguments for help.").BuiltString);
_chat.Print(new SeStringBuilder().AddCommand("reverttoautomation",
"Reverts a given character to its supposed state using automated designs. Use without arguments for help.").BuiltString);
_chat.Print(new SeStringBuilder()
.AddCommand("copy", "Copy the current state of a character to clipboard. Use without arguments for help.").BuiltString);
@ -296,11 +299,11 @@ public class CommandService : IDisposable
return true;
}
private bool ReapplyAutomation(string argument)
private bool ReapplyAutomation(string argument, string command, bool revert)
{
if (argument.Length == 0)
{
_chat.Print(new SeStringBuilder().AddText("Use with /glamour reapplyautomation ").AddGreen("[Character Identifier]").BuiltString);
_chat.Print(new SeStringBuilder().AddText($"Use with /glamour {command} ").AddGreen("[Character Identifier]").BuiltString);
PlayerIdentifierHelp(false, true);
return true;
}
@ -318,7 +321,7 @@ public class CommandService : IDisposable
{
if (_stateManager.GetOrCreate(identifier, actor, out var state))
{
_autoDesignApplier.ReapplyAutomation(actor, identifier, state);
_autoDesignApplier.ReapplyAutomation(actor, identifier, state, revert);
_stateManager.ReapplyState(actor, StateSource.Manual);
}
}
@ -474,7 +477,10 @@ public class CommandService : IDisposable
_chat.Print(new SeStringBuilder()
.AddText(" 》 Clipboard as a single word will try to apply a design string currently in your clipboard.").BuiltString);
_chat.Print(new SeStringBuilder()
.AddText(" 》 ").AddYellow("Random").AddText(" supports many restrictions, see the Restriction Builder when adding a Random design to Automations for valid strings.").BuiltString);
.AddText(" 》 ").AddYellow("Random")
.AddText(
" supports many restrictions, see the Restriction Builder when adding a Random design to Automations for valid strings.")
.BuiltString);
_chat.Print(new SeStringBuilder()
.AddText(" 》 ").AddBlue("<Enable Mods>").AddText(" is optional and can be omitted (together with the ;), ").AddBlue("true")
.AddText(" or ").AddBlue("false").AddText(".").BuiltString);
@ -677,6 +683,7 @@ public class CommandService : IDisposable
_chat.Print(new SeStringBuilder().AddText("No design matched your restrictions.").BuiltString);
return false;
}
_chat.Print($"Chose random design {((Design)design).Name}.");
}
catch (Exception ex)