This commit is contained in:
Ottermandias 2024-01-26 16:10:09 +01:00
parent 2219d9293f
commit 282d6df165
7 changed files with 190 additions and 75 deletions

View file

@ -18,6 +18,16 @@ public enum ApplicationType : byte
public static class ApplicationTypeExtensions public static class ApplicationTypeExtensions
{ {
public static readonly IReadOnlyList<(ApplicationType, string)> Types = new[]
{
(ApplicationType.Customizations,
"Apply all customization changes that are enabled in this design and that are valid in a fixed design and for the given race and gender."),
(ApplicationType.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
(ApplicationType.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
(ApplicationType.GearCustomization, "Apply all dye and crest changes that are enabled in this design."),
(ApplicationType.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
};
public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat( public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat(
this ApplicationType type, DesignBase? design) this ApplicationType type, DesignBase? design)
{ {

View file

@ -15,4 +15,5 @@ public enum LinkOrder : byte
Self, Self,
After, After,
Before, Before,
None,
}; };

View file

@ -56,6 +56,17 @@ public sealed class DesignLinkManager : IService, IDisposable
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null); _event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
} }
public void ChangeApplicationType(Design parent, int idx, LinkOrder order, ApplicationType applicationType)
{
applicationType &= ApplicationType.All;
if (!parent.Links.ChangeApplicationRules(idx, order, applicationType, out var old))
return;
_saveService.QueueSave(parent);
Glamourer.Log.Debug($"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}.");
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
}
private void OnDesignChanged(DesignChanged.Type type, Design deletedDesign, object? _) private void OnDesignChanged(DesignChanged.Type type, Design deletedDesign, object? _)
{ {
if (type is not DesignChanged.Type.Deleted) if (type is not DesignChanged.Type.Deleted)
@ -63,12 +74,12 @@ public sealed class DesignLinkManager : IService, IDisposable
foreach (var design in _storage) foreach (var design in _storage)
{ {
if (design.Links.Remove(deletedDesign)) if (!design.Links.Remove(deletedDesign))
{ continue;
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion."); Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion.");
_saveService.QueueSave(design); _saveService.QueueSave(design);
} }
} }
} }
}

View file

@ -57,29 +57,45 @@ public sealed class LinkContainer : List<DesignLink>
return true; return true;
} }
public bool ChangeApplicationRules(int idx, LinkOrder order, ApplicationType type, out ApplicationType old)
{
var list = order switch
{
LinkOrder.Before => Before,
LinkOrder.After => After,
_ => throw new ArgumentException("Invalid link order."),
};
old = list[idx].Type;
if (idx < 0 || idx >= list.Count || old == type)
return false;
list[idx] = list[idx] with { Type = type };
return true;
}
public static bool CanAddLink(Design parent, Design child, LinkOrder order, out string error) public static bool CanAddLink(Design parent, Design child, LinkOrder order, out string error)
{ {
if (parent == child) if (parent == child)
{ {
error = $"Can not link {parent.Identifier} with itself."; error = $"Can not link {parent.Incognito} with itself.";
return false; return false;
} }
if (parent.Links.Contains(child)) if (parent.Links.Contains(child))
{ {
error = $"Design {parent.Identifier} already contains a direct link to {child.Identifier}."; error = $"Design {parent.Incognito} already contains a direct link to {child.Incognito}.";
return false; return false;
} }
if (GetAllLinks(parent).Any(l => l.Link.Link == child && l.Order != order)) if (GetAllLinks(parent).Any(l => l.Link.Link == child && l.Order != order))
{ {
error = $"Adding {child.Identifier} to {parent.Identifier}s links would create a circle, the parent already links to the child in the opposite direction."; error = $"Adding {child.Incognito} to {parent.Incognito}s links would create a circle, the parent already links to the child in the opposite direction.";
return false; return false;
} }
if (GetAllLinks(child).Any(l => l.Link.Link == parent && l.Order == order)) if (GetAllLinks(child).Any(l => l.Link.Link == parent && l.Order == order))
{ {
error = $"Adding {child.Identifier} to {parent.Identifier}s links would create a circle, the child already links to the parent in the opposite direction."; error = $"Adding {child.Incognito} to {parent.Incognito}s links would create a circle, the child already links to the parent in the opposite direction.";
return false; return false;
} }
@ -161,7 +177,7 @@ public sealed class LinkContainer : List<DesignLink>
var after = new JArray(); var after = new JArray();
foreach (var link in After) foreach (var link in After)
{ {
before.Add(new JObject after.Add(new JObject
{ {
["Design"] = link.Link.Identifier, ["Design"] = link.Link.Identifier,
["Type"] = (uint)link.Type, ["Type"] = (uint)link.Type,

View file

@ -384,7 +384,7 @@ public class SetPanel(
{ {
void Box(int idx) void Box(int idx)
{ {
var (type, description) = Types[idx]; var (type, description) = ApplicationTypeExtensions.Types[idx];
var value = design.Type.HasFlag(type); var value = design.Type.HasFlag(type);
if (ImGui.Checkbox($"##{(byte)type}", ref value)) if (ImGui.Checkbox($"##{(byte)type}", ref value))
newType = value ? newType | type : newType & ~type; newType = value ? newType | type : newType & ~type;
@ -428,40 +428,20 @@ public class SetPanel(
_manager.ChangeIdentifier(setIndex, _identifierDrawer.MannequinIdentifier); _manager.ChangeIdentifier(setIndex, _identifierDrawer.MannequinIdentifier);
} }
private sealed class JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log)
private static readonly IReadOnlyList<(ApplicationType, string)> Types = new[] : FilterComboCache<JobGroup>(() => jobs.JobGroups.Values.ToList(), log)
{ {
(ApplicationType.Customizations,
"Apply all customization changes that are enabled in this design and that are valid in a fixed design and for the given race and gender."),
(ApplicationType.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
(ApplicationType.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
(ApplicationType.GearCustomization, "Apply all dye and crest changes that are enabled in this design."),
(ApplicationType.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
};
private sealed class JobGroupCombo : FilterComboCache<JobGroup>
{
private readonly AutoDesignManager _manager;
private readonly JobService _jobs;
public JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log)
: base(() => jobs.JobGroups.Values.ToList(), log)
{
_manager = manager;
_jobs = jobs;
}
public void Draw(AutoDesignSet set, AutoDesign design, int autoDesignIndex) public void Draw(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
{ {
CurrentSelection = design.Jobs; CurrentSelection = design.Jobs;
CurrentSelectionIdx = _jobs.JobGroups.Values.IndexOf(j => j.Id == design.Jobs.Id); CurrentSelectionIdx = jobs.JobGroups.Values.IndexOf(j => j.Id == design.Jobs.Id);
if (Draw("##JobGroups", design.Jobs.Name, if (Draw("##JobGroups", design.Jobs.Name,
"Select for which job groups this design should be applied.\nControl + Right-Click to set to all classes.", "Select for which job groups this design should be applied.\nControl + Right-Click to set to all classes.",
ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeightWithSpacing()) ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeightWithSpacing())
&& CurrentSelectionIdx >= 0) && CurrentSelectionIdx >= 0)
_manager.ChangeJobCondition(set, autoDesignIndex, CurrentSelection); manager.ChangeJobCondition(set, autoDesignIndex, CurrentSelection);
else if (ImGui.GetIO().KeyCtrl && ImGui.IsItemClicked(ImGuiMouseButton.Right)) else if (ImGui.GetIO().KeyCtrl && ImGui.IsItemClicked(ImGuiMouseButton.Right))
_manager.ChangeJobCondition(set, autoDesignIndex, _jobs.JobGroups[1]); manager.ChangeJobCondition(set, autoDesignIndex, jobs.JobGroups[1]);
} }
protected override string ToString(JobGroup obj) protected override string ToString(JobGroup obj)

View file

@ -1,4 +1,6 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.Links; using Glamourer.Designs.Links;
using ImGuiNET; using ImGuiNET;
@ -11,9 +13,9 @@ namespace Glamourer.Gui.Tabs.DesignTab;
public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, DesignCombo _combo) : IUiService public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, DesignCombo _combo) : IUiService
{ {
private int _dragDropIndex = -1; private int _dragDropIndex = -1;
private LinkOrder _dragDropOrder = LinkOrder.Self; private LinkOrder _dragDropOrder = LinkOrder.None;
private int _dragDropTargetIndex = -1; private int _dragDropTargetIndex = -1;
private LinkOrder _dragDropTargetOrder = LinkOrder.Self; private LinkOrder _dragDropTargetOrder = LinkOrder.None;
public void Draw() public void Draw()
{ {
@ -21,30 +23,76 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
if (!header) if (!header)
return; return;
var width = ImGui.GetContentRegionAvail().X / 2; DrawList();
DrawList(_selector.Selected!.Links.Before, LinkOrder.Before, width);
ImGui.SameLine();
DrawList(_selector.Selected!.Links.After, LinkOrder.After, width);
if (_dragDropTargetIndex < 0
|| _dragDropIndex < 0)
return;
_linkManager.MoveDesignLink(_selector.Selected!, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder);
_dragDropIndex = -1;
_dragDropTargetIndex = -1;
_dragDropOrder = LinkOrder.Self;
_dragDropTargetOrder = LinkOrder.Self;
} }
private void DrawList(IReadOnlyList<DesignLink> list, LinkOrder order, float width) private void MoveLink()
{ {
using var id = ImRaii.PushId((int)order); if (_dragDropTargetIndex < 0 || _dragDropIndex < 0)
using var table = ImRaii.Table("table", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter, return;
new Vector2(width, list.Count * ImGui.GetFrameHeightWithSpacing()));
if (_dragDropOrder is LinkOrder.Self)
switch (_dragDropTargetOrder)
{
case LinkOrder.Before:
for (var i = _selector.Selected!.Links.Before.Count - 1; i >= _dragDropTargetIndex; --i)
_linkManager.MoveDesignLink(_selector.Selected!, i, LinkOrder.Before, 0, LinkOrder.After);
break;
case LinkOrder.After:
for (var i = 0; i <= _dragDropTargetIndex; ++i)
{
_linkManager.MoveDesignLink(_selector.Selected!, 0, LinkOrder.After, _selector.Selected!.Links.Before.Count,
LinkOrder.Before);
}
break;
}
else if (_dragDropTargetOrder is LinkOrder.Self)
_linkManager.MoveDesignLink(_selector.Selected!, _dragDropIndex, _dragDropOrder, _selector.Selected!.Links.Before.Count,
LinkOrder.Before);
else
_linkManager.MoveDesignLink(_selector.Selected!, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder);
_dragDropIndex = -1;
_dragDropTargetIndex = -1;
_dragDropOrder = LinkOrder.None;
_dragDropTargetOrder = LinkOrder.None;
}
private void DrawList()
{
using var table = ImRaii.Table("table", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter);
if (!table) if (!table)
return; return;
ImGui.TableSetupColumn("Del", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight());
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Detail", ImGuiTableColumnFlags.WidthFixed,
6 * ImGui.GetFrameHeight() + 5 * ImGui.GetStyle().ItemInnerSpacing.X);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing);
DrawSubList(_selector.Selected!.Links.Before, LinkOrder.Before);
DrawSelf();
DrawSubList(_selector.Selected!.Links.After, LinkOrder.After);
DrawNew();
MoveLink();
}
private void DrawSelf()
{
using var id = ImRaii.PushId((int)LinkOrder.Self);
ImGui.TableNextColumn();
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.Selectable(_selector.IncognitoMode ? _selector.Selected!.Incognito : _selector.Selected!.Name.Text);
DrawDragDrop(_selector.Selected!, LinkOrder.Self, 0);
ImGui.TableNextColumn();
}
private void DrawSubList(IReadOnlyList<DesignLink> list, LinkOrder order)
{
using var id = ImRaii.PushId((int)order);
var buttonSize = new Vector2(ImGui.GetFrameHeight()); var buttonSize = new Vector2(ImGui.GetFrameHeight());
for (var i = 0; i < list.Count; ++i) for (var i = 0; i < list.Count; ++i)
{ {
@ -52,11 +100,6 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var delete = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, "Delete this link.", false, true); var delete = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, "Delete this link.", false, true);
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted($"#{i:D2}");
var (design, flags) = list[i]; var (design, flags) = list[i];
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -66,32 +109,48 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(flags.ToString()); DrawApplicationBoxes(i, order, flags);
if (delete) if (delete)
_linkManager.RemoveDesignLink(_selector.Selected!, i--, order); _linkManager.RemoveDesignLink(_selector.Selected!, i--, order);
} }
}
private void DrawNew()
{
var buttonSize = new Vector2(ImGui.GetFrameHeight());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
string tt; ImGui.TableNextColumn();
bool canAdd; _combo.Draw(ImGui.GetContentRegionAvail().X);
ImGui.TableNextColumn();
string ttBefore, ttAfter;
bool canAddBefore, canAddAfter;
if (_combo.Design == null) if (_combo.Design == null)
{ {
tt = "Select a design first."; ttAfter = ttBefore = "Select a design first.";
canAdd = false; canAddBefore = canAddAfter = false;
} }
else else
{ {
canAdd = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, order, out var error); canAddBefore = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, LinkOrder.Before, out var error);
tt = canAdd ? $"Add a link to {_combo.Design.Name}." : $"Can not add a link to {_combo.Design.Name}: {error}"; ttBefore = canAddBefore
? $"Add a link at the top of the list to {_combo.Design.Name}."
: $"Can not add a link to {_combo.Design.Name}:\n{error}";
canAddAfter = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, LinkOrder.After, out error);
ttAfter = canAddAfter
? $"Add a link at the bottom of the list to {_combo.Design.Name}."
: $"Can not add a link to {_combo.Design.Name}:\n{error}";
} }
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), buttonSize, tt, !canAdd, true)) if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleUp.ToIconString(), buttonSize, ttBefore, !canAddBefore, true))
_linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, order); {
_linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, LinkOrder.Before);
_linkManager.MoveDesignLink(_selector.Selected!, _selector.Selected!.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before);
}
ImGui.TableNextColumn(); ImGui.SameLine();
ImGui.TableNextColumn(); if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleDown.ToIconString(), buttonSize, ttAfter, !canAddAfter, true))
_combo.Draw(200); _linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, LinkOrder.After);
} }
private void DrawDragDrop(Design design, LinkOrder order, int index) private void DrawDragDrop(Design design, LinkOrder order, int index)
@ -117,4 +176,42 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
_dragDropTargetIndex = index; _dragDropTargetIndex = index;
_dragDropTargetOrder = order; _dragDropTargetOrder = order;
} }
private void DrawApplicationBoxes(int idx, LinkOrder order, ApplicationType current)
{
var newType = current;
var newTypeInt = (uint)newType;
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale))
{
using var _ = ImRaii.PushColor(ImGuiCol.Border, ColorId.FolderLine.Value());
if (ImGui.CheckboxFlags("##all", ref newTypeInt, (uint)ApplicationType.All))
newType = (ApplicationType)newTypeInt;
}
ImGuiUtil.HoverTooltip("Toggle all application modes at once.");
ImGui.SameLine();
Box(0);
ImGui.SameLine();
Box(1);
ImGui.SameLine();
Box(2);
ImGui.SameLine();
Box(3);
ImGui.SameLine();
Box(4);
if (newType != current)
_linkManager.ChangeApplicationType(_selector.Selected!, idx, order, current);
return;
void Box(int i)
{
var (applicationType, description) = ApplicationTypeExtensions.Types[i];
var value = applicationType.HasFlag(applicationType);
if (ImGui.Checkbox($"##{(byte)applicationType}", ref value))
newType = value ? newType | applicationType : newType & ~applicationType;
ImGuiUtil.HoverTooltip(description);
}
}
} }

View file

@ -303,7 +303,7 @@ public class DesignPanel(
{ {
var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selector.Selected!.DoApplyMeta(index); var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selector.Selected!.DoApplyMeta(index);
if (ImGui.Checkbox(label, ref apply) || bigChange) if (ImGui.Checkbox(label, ref apply) || bigChange)
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply); _manager.ChangeApplyMeta(_selector.Selected!, index, apply);
} }
} }