413 lines
15 KiB
C#
Executable File
413 lines
15 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Interface;
|
|
using HUD_Manager.Configuration;
|
|
using HUD_Manager.Structs;
|
|
using HUD_Manager.Tree;
|
|
using HUD_Manager.Ui.Editor.Tabs;
|
|
using ImGuiNET;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace HUD_Manager.Ui.Editor {
|
|
public class LayoutEditor {
|
|
private Plugin Plugin { get; }
|
|
private Interface Ui { get; }
|
|
internal Previews Previews { get; }
|
|
private HudElements HudElements { get; }
|
|
private Windows Windows { get; }
|
|
|
|
private string? RenameLayoutName { get; set; }
|
|
private string? NewLayoutName { get; set; }
|
|
private string? ImportLayoutName { get; set; }
|
|
|
|
private float _dragSpeed = 1f;
|
|
internal float DragSpeed => this._dragSpeed;
|
|
|
|
public LayoutEditor(Plugin plugin, Interface ui) {
|
|
this.Plugin = plugin;
|
|
this.Ui = ui;
|
|
|
|
this.Previews = new Previews(plugin, ui);
|
|
this.HudElements = new HudElements(plugin, ui, this);
|
|
this.Windows = new Windows(plugin);
|
|
}
|
|
|
|
internal void Draw() {
|
|
if (!ImGui.BeginTabItem("Layout editor")) {
|
|
return;
|
|
}
|
|
|
|
if (this.Plugin.Config.SwapsEnabled) {
|
|
ImGui.TextUnformatted("Cannot edit layouts while swaps are enabled.");
|
|
|
|
if (ImGui.Button("Disable swaps")) {
|
|
this.Plugin.Config.SwapsEnabled = false;
|
|
this.Plugin.Config.Save();
|
|
}
|
|
|
|
goto EndTabItem;
|
|
}
|
|
|
|
var charConfig = this.Plugin.Interface.Framework.Gui.GetAddonByName("ConfigCharacter", 1);
|
|
if (charConfig != null && charConfig.Visible) {
|
|
ImGui.TextUnformatted("Please close the Character Configuration window before continuing.");
|
|
goto EndTabItem;
|
|
}
|
|
|
|
var update = false;
|
|
|
|
this.Previews.Draw(ref update);
|
|
|
|
ImGui.TextUnformatted("Layout");
|
|
|
|
var nodes = Node<SavedLayout>.BuildTree(this.Plugin.Config.Layouts);
|
|
|
|
this.Plugin.Config.Layouts.TryGetValue(this.Ui.SelectedLayout, out var selected);
|
|
var selectedName = selected?.Name ?? "<none>";
|
|
|
|
if (ImGui.BeginCombo("##edit-layout", selectedName)) {
|
|
if (ImGui.Selectable("<none>")) {
|
|
this.Ui.SelectedLayout = Guid.Empty;
|
|
}
|
|
|
|
foreach (var node in nodes) {
|
|
foreach (var (child, depth) in node.TraverseWithDepth()) {
|
|
var indent = new string(' ', (int) depth * 4);
|
|
if (!ImGui.Selectable($"{indent}{child.Value.Name}##edit-{child.Id}", child.Id == this.Ui.SelectedLayout)) {
|
|
continue;
|
|
}
|
|
|
|
this.Ui.SelectedLayout = child.Id;
|
|
update = true;
|
|
}
|
|
}
|
|
|
|
ImGui.EndCombo();
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.Plus, "uimanager-add-layout")) {
|
|
ImGui.OpenPopup(Popups.AddLayout);
|
|
}
|
|
|
|
ImGuiExt.HoverTooltip("Add a new layout");
|
|
|
|
this.SetUpAddLayoutPopup();
|
|
|
|
ImGui.SameLine();
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.TrashAlt, "uimanager-delete-layout") && this.Ui.SelectedLayout != Guid.Empty) {
|
|
ImGui.OpenPopup(Popups.DeleteVerify);
|
|
}
|
|
|
|
this.SetUpDeleteVerifyPopup(nodes);
|
|
|
|
ImGuiExt.HoverTooltip("Delete the selected layout");
|
|
|
|
ImGui.SameLine();
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.PencilAlt, "uimanager-rename-layout") && this.Ui.SelectedLayout != Guid.Empty) {
|
|
this.RenameLayoutName = this.Plugin.Config.Layouts[this.Ui.SelectedLayout].Name;
|
|
ImGui.OpenPopup(Popups.RenameLayout);
|
|
}
|
|
|
|
ImGuiExt.HoverTooltip("Rename the selected layout");
|
|
|
|
this.SetUpRenameLayoutPopup();
|
|
|
|
ImGui.SameLine();
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.FileImport, "uimanager-import-layout")) {
|
|
ImGui.OpenPopup(Popups.ImportLayout);
|
|
}
|
|
|
|
ImGuiExt.HoverTooltip("Import a layout from an in-game HUD slot or the clipboard");
|
|
|
|
this.SetUpImportLayoutPopup();
|
|
|
|
ImGui.SameLine();
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.FileExport, "uimanager-export-layout")) {
|
|
ImGui.OpenPopup(Popups.ExportLayout);
|
|
}
|
|
|
|
ImGuiExt.HoverTooltip("Export a layout to an in-game HUD slot or the clipboard");
|
|
|
|
this.SetUpExportLayoutPopup();
|
|
|
|
if (this.Ui.SelectedLayout == Guid.Empty) {
|
|
goto EndTabItem;
|
|
}
|
|
|
|
var layout = this.Plugin.Config.Layouts[this.Ui.SelectedLayout];
|
|
|
|
this.Plugin.Config.Layouts.TryGetValue(layout.Parent, out var parent);
|
|
var parentName = parent?.Name ?? "<none>";
|
|
|
|
var ourChildren = nodes.Find(this.Ui.SelectedLayout)
|
|
?.Traverse()
|
|
.Select(el => el.Id)
|
|
.ToArray() ?? new Guid[0];
|
|
|
|
if (ImGui.BeginCombo("Parent", parentName)) {
|
|
if (ImGui.Selectable("<none>")) {
|
|
layout.Parent = Guid.Empty;
|
|
this.Plugin.Config.Save();
|
|
}
|
|
|
|
foreach (var node in nodes) {
|
|
foreach (var (child, depth) in node.TraverseWithDepth()) {
|
|
var selectedParent = child.Id == this.Ui.SelectedLayout;
|
|
var disabled = selectedParent || ourChildren.Contains(child.Id);
|
|
var flags = disabled ? ImGuiSelectableFlags.Disabled : ImGuiSelectableFlags.None;
|
|
|
|
var indent = new string(' ', (int) depth * 4);
|
|
if (!ImGui.Selectable($"{indent}{child.Value.Name}##parent-{child.Id}", selectedParent, flags)) {
|
|
continue;
|
|
}
|
|
|
|
layout.Parent = child.Id;
|
|
this.Plugin.Config.Save();
|
|
}
|
|
}
|
|
|
|
ImGui.EndCombo();
|
|
}
|
|
|
|
ImGui.Separator();
|
|
|
|
if (ImGui.CollapsingHeader("Options##uimanager-options")) {
|
|
ImGui.DragFloat("Slider speed", ref this._dragSpeed, 0.01f, 0.01f, 10f);
|
|
|
|
if (ImGui.BeginCombo("Positioning mode", this.Plugin.Config.PositioningMode.ToString())) {
|
|
foreach (var mode in (PositioningMode[]) Enum.GetValues(typeof(PositioningMode))) {
|
|
if (!ImGui.Selectable($"{mode}##positioning", this.Plugin.Config.PositioningMode == mode)) {
|
|
continue;
|
|
}
|
|
|
|
this.Plugin.Config.PositioningMode = mode;
|
|
this.Plugin.Config.Save();
|
|
}
|
|
|
|
ImGui.EndCombo();
|
|
}
|
|
}
|
|
|
|
ImGui.Separator();
|
|
|
|
if (ImGui.BeginTabBar("uimanager-positioning")) {
|
|
if (ImGui.BeginTabItem("HUD Elements")) {
|
|
this.HudElements.Draw(layout, ref update);
|
|
|
|
ImGui.EndTabItem();
|
|
}
|
|
|
|
if (ImGui.BeginTabItem("Windows")) {
|
|
this.Windows.Draw(layout);
|
|
|
|
ImGui.EndTabItem();
|
|
}
|
|
|
|
ImGui.EndTabBar();
|
|
}
|
|
|
|
EndTabItem:
|
|
ImGui.EndTabItem();
|
|
}
|
|
|
|
private void SetUpAddLayoutPopup() {
|
|
if (!ImGui.BeginPopup(Popups.AddLayout)) {
|
|
return;
|
|
}
|
|
|
|
var name = this.NewLayoutName ?? string.Empty;
|
|
if (ImGui.InputText("Name", ref name, 100)) {
|
|
this.NewLayoutName = string.IsNullOrWhiteSpace(name) ? null : name;
|
|
}
|
|
|
|
var exists = this.Plugin.Config.Layouts.Values.Any(layout => layout.Name == this.NewLayoutName);
|
|
if (exists) {
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
|
|
ImGui.TextUnformatted("A layout with that name already exists.");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
|
|
if (!exists && ImGui.Button("Add") && this.NewLayoutName != null) {
|
|
// create the layout
|
|
var saved = new SavedLayout(this.NewLayoutName, new Dictionary<ElementKind, Element>(), new Dictionary<string, Window>(), Guid.Empty);
|
|
// reset the new layout name
|
|
this.NewLayoutName = null;
|
|
|
|
// generate a new id
|
|
var id = Guid.NewGuid();
|
|
|
|
// add the layout and save the config
|
|
this.Plugin.Config.Layouts[id] = saved;
|
|
this.Plugin.Config.Save();
|
|
|
|
// switch the editor to the new layout
|
|
this.Ui.SelectedLayout = id;
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
private void SetUpDeleteVerifyPopup(IEnumerable<Node<SavedLayout>> nodes) {
|
|
if (!ImGui.BeginPopupModal(Popups.DeleteVerify)) {
|
|
return;
|
|
}
|
|
|
|
if (this.Plugin.Config.Layouts.TryGetValue(this.Ui.SelectedLayout, out var deleting)) {
|
|
ImGui.TextUnformatted($"Are you sure you want to delete the layout \"{deleting.Name}\"?");
|
|
|
|
if (ImGui.Button("Yes")) {
|
|
// unset the parent of any child layouts
|
|
var node = nodes.Find(this.Ui.SelectedLayout);
|
|
if (node != null) {
|
|
foreach (var child in node.Children) {
|
|
child.Parent = null;
|
|
child.Value.Parent = Guid.Empty;
|
|
}
|
|
}
|
|
|
|
this.Plugin.Config.HudConditionMatches.RemoveAll(match => match.LayoutId == this.Ui.SelectedLayout);
|
|
|
|
this.Plugin.Config.Layouts.Remove(this.Ui.SelectedLayout);
|
|
this.Ui.SelectedLayout = Guid.Empty;
|
|
this.Plugin.Config.Save();
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
if (ImGui.Button("No")) {
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
private void SetUpRenameLayoutPopup() {
|
|
if (!ImGui.BeginPopup(Popups.RenameLayout)) {
|
|
return;
|
|
}
|
|
|
|
var name = this.RenameLayoutName ?? "<none>";
|
|
if (ImGui.InputText("Name", ref name, 100)) {
|
|
this.RenameLayoutName = string.IsNullOrWhiteSpace(name) ? null : name;
|
|
}
|
|
|
|
if (ImGui.Button("Rename") && this.RenameLayoutName != null) {
|
|
this.Plugin.Config.Layouts[this.Ui.SelectedLayout].Name = this.RenameLayoutName;
|
|
this.Plugin.Config.Save();
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
private void SetUpImportLayoutPopup() {
|
|
if (!ImGui.BeginPopup(Popups.ImportLayout)) {
|
|
return;
|
|
}
|
|
|
|
var importName = this.ImportLayoutName ?? "";
|
|
if (ImGui.InputText("Imported layout name", ref importName, 100)) {
|
|
this.ImportLayoutName = string.IsNullOrWhiteSpace(importName) ? null : importName;
|
|
}
|
|
|
|
var exists = this.Plugin.Config.Layouts.Values.Any(layout => layout.Name == this.ImportLayoutName);
|
|
if (exists) {
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, .8f, .2f, 1f));
|
|
ImGui.TextUnformatted("This will overwrite an existing layout.");
|
|
ImGui.PopStyleColor();
|
|
}
|
|
|
|
var current = this.Plugin.Hud.GetActiveHudSlot();
|
|
foreach (var slot in (HudSlot[]) Enum.GetValues(typeof(HudSlot))) {
|
|
var name = current == slot ? $"({(int) slot + 1})" : $"{(int) slot + 1}";
|
|
if (ImGui.Button($"{name}##import-{slot}") && this.ImportLayoutName != null) {
|
|
Guid id;
|
|
string newName;
|
|
Dictionary<string, Window> windows;
|
|
if (exists) {
|
|
var overwriting = this.Plugin.Config.Layouts.First(entry => entry.Value.Name == this.ImportLayoutName);
|
|
id = overwriting.Key;
|
|
newName = overwriting.Value.Name;
|
|
windows = overwriting.Value.Windows;
|
|
} else {
|
|
id = Guid.NewGuid();
|
|
newName = this.ImportLayoutName;
|
|
windows = new Dictionary<string, Window>();
|
|
}
|
|
|
|
var currentLayout = this.Plugin.Hud.ReadLayout(slot);
|
|
var newLayout = new SavedLayout(newName, currentLayout, windows);
|
|
this.Plugin.Config.Layouts[id] = newLayout;
|
|
this.Plugin.Config.Save();
|
|
|
|
this.Ui.SelectedLayout = id;
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
}
|
|
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.Clipboard, "import-clipboard") && this.ImportLayoutName != null) {
|
|
SavedLayout? saved;
|
|
try {
|
|
saved = JsonConvert.DeserializeObject<SavedLayout>(ImGui.GetClipboardText());
|
|
} catch (Exception) {
|
|
saved = null;
|
|
}
|
|
|
|
if (saved != null) {
|
|
saved.Name = this.ImportLayoutName;
|
|
|
|
var id = Guid.NewGuid();
|
|
this.Plugin.Config.Layouts[id] = saved;
|
|
this.Plugin.Config.Save();
|
|
|
|
this.Ui.SelectedLayout = id;
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
|
|
private void SetUpExportLayoutPopup() {
|
|
if (!ImGui.BeginPopup(Popups.ExportLayout)) {
|
|
return;
|
|
}
|
|
|
|
if (!this.Plugin.Config.Layouts.TryGetValue(this.Ui.SelectedLayout, out var layout)) {
|
|
return;
|
|
}
|
|
|
|
var current = this.Plugin.Hud.GetActiveHudSlot();
|
|
foreach (var slot in (HudSlot[]) Enum.GetValues(typeof(HudSlot))) {
|
|
var name = current == slot ? $"({(int) slot + 1})" : $"{(int) slot + 1}";
|
|
if (ImGui.Button($"{name}##export-{slot}")) {
|
|
this.Plugin.Hud.WriteEffectiveLayout(slot, this.Ui.SelectedLayout);
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
}
|
|
|
|
if (ImGuiExt.IconButton(FontAwesomeIcon.Clipboard, "export-clipboard")) {
|
|
var json = JsonConvert.SerializeObject(layout);
|
|
ImGui.SetClipboardText(json);
|
|
}
|
|
|
|
ImGui.EndPopup();
|
|
}
|
|
}
|
|
}
|