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 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(
this ApplicationType type, DesignBase? design)
{

View file

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

View file

@ -56,6 +56,17 @@ public sealed class DesignLinkManager : IService, IDisposable
_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? _)
{
if (type is not DesignChanged.Type.Deleted)
@ -63,12 +74,12 @@ public sealed class DesignLinkManager : IService, IDisposable
foreach (var design in _storage)
{
if (design.Links.Remove(deletedDesign))
{
design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion.");
_saveService.QueueSave(design);
}
if (!design.Links.Remove(deletedDesign))
continue;
design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion.");
_saveService.QueueSave(design);
}
}
}

View file

@ -57,29 +57,45 @@ public sealed class LinkContainer : List<DesignLink>
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)
{
if (parent == child)
{
error = $"Can not link {parent.Identifier} with itself.";
error = $"Can not link {parent.Incognito} with itself.";
return false;
}
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;
}
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;
}
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;
}
@ -161,7 +177,7 @@ public sealed class LinkContainer : List<DesignLink>
var after = new JArray();
foreach (var link in After)
{
before.Add(new JObject
after.Add(new JObject
{
["Design"] = link.Link.Identifier,
["Type"] = (uint)link.Type,

View file

@ -384,7 +384,7 @@ public class SetPanel(
{
void Box(int idx)
{
var (type, description) = Types[idx];
var (type, description) = ApplicationTypeExtensions.Types[idx];
var value = design.Type.HasFlag(type);
if (ImGui.Checkbox($"##{(byte)type}", ref value))
newType = value ? newType | type : newType & ~type;
@ -428,40 +428,20 @@ public class SetPanel(
_manager.ChangeIdentifier(setIndex, _identifierDrawer.MannequinIdentifier);
}
private static readonly IReadOnlyList<(ApplicationType, string)> Types = new[]
private sealed class JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log)
: 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)
{
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,
"Select for which job groups this design should be applied.\nControl + Right-Click to set to all classes.",
ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeightWithSpacing())
&& CurrentSelectionIdx >= 0)
_manager.ChangeJobCondition(set, autoDesignIndex, CurrentSelection);
manager.ChangeJobCondition(set, autoDesignIndex, CurrentSelection);
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)

View file

@ -1,4 +1,6 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Designs.Links;
using ImGuiNET;
@ -11,9 +13,9 @@ namespace Glamourer.Gui.Tabs.DesignTab;
public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, DesignCombo _combo) : IUiService
{
private int _dragDropIndex = -1;
private LinkOrder _dragDropOrder = LinkOrder.Self;
private LinkOrder _dragDropOrder = LinkOrder.None;
private int _dragDropTargetIndex = -1;
private LinkOrder _dragDropTargetOrder = LinkOrder.Self;
private LinkOrder _dragDropTargetOrder = LinkOrder.None;
public void Draw()
{
@ -21,30 +23,76 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
if (!header)
return;
var width = ImGui.GetContentRegionAvail().X / 2;
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;
DrawList();
}
private void DrawList(IReadOnlyList<DesignLink> list, LinkOrder order, float width)
private void MoveLink()
{
using var id = ImRaii.PushId((int)order);
using var table = ImRaii.Table("table", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter,
new Vector2(width, list.Count * ImGui.GetFrameHeightWithSpacing()));
if (_dragDropTargetIndex < 0 || _dragDropIndex < 0)
return;
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)
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());
for (var i = 0; i < list.Count; ++i)
{
@ -52,11 +100,6 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
ImGui.TableNextColumn();
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];
ImGui.TableNextColumn();
@ -66,32 +109,48 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(flags.ToString());
DrawApplicationBoxes(i, order, flags);
if (delete)
_linkManager.RemoveDesignLink(_selector.Selected!, i--, order);
}
}
private void DrawNew()
{
var buttonSize = new Vector2(ImGui.GetFrameHeight());
ImGui.TableNextColumn();
string tt;
bool canAdd;
ImGui.TableNextColumn();
_combo.Draw(ImGui.GetContentRegionAvail().X);
ImGui.TableNextColumn();
string ttBefore, ttAfter;
bool canAddBefore, canAddAfter;
if (_combo.Design == null)
{
tt = "Select a design first.";
canAdd = false;
ttAfter = ttBefore = "Select a design first.";
canAddBefore = canAddAfter = false;
}
else
{
canAdd = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, order, out var error);
tt = canAdd ? $"Add a link to {_combo.Design.Name}." : $"Can not add a link to {_combo.Design.Name}: {error}";
canAddBefore = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, LinkOrder.Before, out var 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))
_linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, order);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleUp.ToIconString(), buttonSize, ttBefore, !canAddBefore, true))
{
_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.TableNextColumn();
_combo.Draw(200);
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.ArrowCircleDown.ToIconString(), buttonSize, ttAfter, !canAddAfter, true))
_linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, LinkOrder.After);
}
private void DrawDragDrop(Design design, LinkOrder order, int index)
@ -117,4 +176,42 @@ public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSe
_dragDropTargetIndex = index;
_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);
if (ImGui.Checkbox(label, ref apply) || bigChange)
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply);
_manager.ChangeApplyMeta(_selector.Selected!, index, apply);
}
}