From 282d6df1652c202f953c5041ded2ebb556977023 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 26 Jan 2024 16:10:09 +0100 Subject: [PATCH] Add UI. --- Glamourer/Automation/ApplicationType.cs | 10 + Glamourer/Designs/Links/DesignLink.cs | 1 + Glamourer/Designs/Links/DesignLinkManager.cs | 23 ++- Glamourer/Designs/Links/LinkContainer.cs | 26 ++- Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs | 32 +--- .../Gui/Tabs/DesignTab/DesignLinkDrawer.cs | 171 ++++++++++++++---- Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs | 2 +- 7 files changed, 190 insertions(+), 75 deletions(-) diff --git a/Glamourer/Automation/ApplicationType.cs b/Glamourer/Automation/ApplicationType.cs index e99d948..03e3a2d 100644 --- a/Glamourer/Automation/ApplicationType.cs +++ b/Glamourer/Automation/ApplicationType.cs @@ -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) { diff --git a/Glamourer/Designs/Links/DesignLink.cs b/Glamourer/Designs/Links/DesignLink.cs index 2adc055..a9fb805 100644 --- a/Glamourer/Designs/Links/DesignLink.cs +++ b/Glamourer/Designs/Links/DesignLink.cs @@ -15,4 +15,5 @@ public enum LinkOrder : byte Self, After, Before, + None, }; diff --git a/Glamourer/Designs/Links/DesignLinkManager.cs b/Glamourer/Designs/Links/DesignLinkManager.cs index e3fe094..76d9c9a 100644 --- a/Glamourer/Designs/Links/DesignLinkManager.cs +++ b/Glamourer/Designs/Links/DesignLinkManager.cs @@ -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); } } } diff --git a/Glamourer/Designs/Links/LinkContainer.cs b/Glamourer/Designs/Links/LinkContainer.cs index 08a1b6d..ef67688 100644 --- a/Glamourer/Designs/Links/LinkContainer.cs +++ b/Glamourer/Designs/Links/LinkContainer.cs @@ -57,29 +57,45 @@ public sealed class LinkContainer : List 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 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, diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index 2cb1ede..0387d58 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -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(() => 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 - { - 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) diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs index 774ee3c..f45f936 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs @@ -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 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 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); + } + } } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs index f7fc253..83da487 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignPanel.cs @@ -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); } }