feat: add style editor

This commit is contained in:
goat 2021-09-27 17:00:24 +02:00
parent c925611674
commit 0f150bc54b
No known key found for this signature in database
GPG key ID: F18F057873895461
6 changed files with 280 additions and 96 deletions

View file

@ -46,6 +46,7 @@ namespace Dalamud.Interface.Internal
private readonly ScratchpadWindow scratchpadWindow;
private readonly SettingsWindow settingsWindow;
private readonly SelfTestWindow selfTestWindow;
private readonly StyleEditorWindow styleEditorWindow;
private ulong frameCount = 0;
@ -79,6 +80,7 @@ namespace Dalamud.Interface.Internal
this.scratchpadWindow = new ScratchpadWindow() { IsOpen = false };
this.settingsWindow = new SettingsWindow() { IsOpen = false };
this.selfTestWindow = new SelfTestWindow() { IsOpen = false };
this.styleEditorWindow = new StyleEditorWindow() { IsOpen = false };
this.WindowSystem.AddWindow(this.changelogWindow);
this.WindowSystem.AddWindow(this.colorDemoWindow);
@ -93,12 +95,11 @@ namespace Dalamud.Interface.Internal
this.WindowSystem.AddWindow(this.scratchpadWindow);
this.WindowSystem.AddWindow(this.settingsWindow);
this.WindowSystem.AddWindow(this.selfTestWindow);
this.WindowSystem.AddWindow(this.styleEditorWindow);
ImGuiManagedAsserts.AssertsEnabled = true;
Service<InterfaceManager>.Get().Draw += this.OnDraw;
Log.Information("Windows added");
}
/// <summary>
@ -212,6 +213,11 @@ namespace Dalamud.Interface.Internal
/// </summary>
public void OpenSelfTest() => this.selfTestWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="StyleEditorWindow"/>.
/// </summary>
public void OpenStyleEditor() => this.styleEditorWindow.IsOpen = true;
#endregion
#region Close
@ -303,6 +309,11 @@ namespace Dalamud.Interface.Internal
/// </summary>
public void ToggleSelfTestWindow() => this.selfTestWindow.Toggle();
/// <summary>
/// Toggles the <see cref="StyleEditorWindow"/>.
/// </summary>
public void ToggleStyleEditorWindow() => this.selfTestWindow.Toggle();
#endregion
private void OnDraw()
@ -452,6 +463,11 @@ namespace Dalamud.Interface.Internal
this.OpenSelfTest();
}
if (ImGui.MenuItem("Open Style Editor"))
{
this.OpenStyleEditor();
}
ImGui.Separator();
if (ImGui.MenuItem("Unload Dalamud"))

View file

@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using CheapLoc;
using Dalamud.Configuration.Internal;
using Dalamud.Data;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Serilog;
namespace Dalamud.Interface.Internal.Windows.StyleEditor
{
/// <summary>
/// Window for the Dalamud style editor.
/// </summary>
public class StyleEditorWindow : Window
{
private ImGuiColorEditFlags alphaFlags = ImGuiColorEditFlags.None;
private StyleModel workStyle = StyleModel.DalamudStandard;
private int currentSel = 0;
private string initialStyle = string.Empty;
private bool didSave = false;
private string renameText = string.Empty;
private bool renameModalDrawing = false;
/// <summary>
/// Initializes a new instance of the <see cref="StyleEditorWindow"/> class.
/// </summary>
public StyleEditorWindow()
: base("Dalamud Style Editor")
{
this.IsOpen = true;
this.SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(890, 560),
MaximumSize = new Vector2(10000, 10000),
};
}
/// <inheritdoc />
public override void OnOpen()
{
this.didSave = false;
var config = Service<DalamudConfiguration>.Get();
config.SavedStyles ??= new List<StyleModel>();
this.currentSel = config.SavedStyles.FindIndex(x => x.Name == config.ChosenStyle);
this.initialStyle = config.ChosenStyle;
base.OnOpen();
}
/// <inheritdoc />
public override void OnClose()
{
if (!this.didSave)
{
var config = Service<DalamudConfiguration>.Get();
var newStyle = config.SavedStyles.FirstOrDefault(x => x.Name == this.initialStyle);
newStyle?.Apply();
}
base.OnClose();
}
/// <inheritdoc />
public override void Draw()
{
var config = Service<DalamudConfiguration>.Get();
var renameModalTitle = Loc.Localize("RenameStyleModalTitle", "Rename Style");
var appliedThisFrame = false;
var styleAry = config.SavedStyles.Select(x => x.Name).ToArray();
ImGui.Text("Choose Style:");
if (ImGui.Combo("###styleChooserCombo", ref this.currentSel, styleAry, styleAry.Length))
{
var newStyle = config.SavedStyles[this.currentSel];
newStyle.Apply();
appliedThisFrame = true;
}
if (ImGui.Button("Add new style"))
{
var newStyle = StyleModel.DalamudStandard;
newStyle.Name = GetRandomName();
config.SavedStyles.Add(newStyle);
this.currentSel = config.SavedStyles.Count - 1;
newStyle.Apply();
appliedThisFrame = true;
config.Save();
}
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash) && this.currentSel != 0)
{
this.currentSel--;
var newStyle = config.SavedStyles[this.currentSel];
newStyle.Apply();
appliedThisFrame = true;
config.SavedStyles.RemoveAt(this.currentSel + 1);
config.Save();
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Delete current style");
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Pen) && this.currentSel != 0)
{
var newStyle = config.SavedStyles[this.currentSel];
this.renameText = newStyle.Name;
this.renameModalDrawing = true;
ImGui.OpenPopup(renameModalTitle);
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Rename style");
ImGui.SameLine();
ImGuiHelpers.ScaledDummy(5);
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.FileExport))
{
var newStyle = config.SavedStyles[this.currentSel];
ImGui.SetClipboardText(newStyle.ToEncoded());
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Copy style to clipboard for sharing");
ImGui.SameLine();
if (ImGuiComponents.IconButton(FontAwesomeIcon.FileImport))
{
var styleJson = ImGui.GetClipboardText();
try
{
var newStyle = StyleModel.FromEncoded(styleJson);
newStyle.Name ??= GetRandomName();
config.SavedStyles.Add(newStyle);
newStyle.Apply();
appliedThisFrame = true;
Log.Information("Applying: " + newStyle.Name);
this.currentSel = config.SavedStyles.Count - 1;
config.Save();
}
catch (Exception ex)
{
Log.Error(ex, "Could not import style");
}
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Import style from clipboard");
ImGui.Separator();
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.50f);
if (this.currentSel == 0)
{
ImGui.TextColored(ImGuiColors.DalamudRed, "You cannot edit the \"Dalamud Standard\" style. Please add a new style first.");
}
else if (appliedThisFrame)
{
ImGui.Text("Applying style...");
}
else if (ImGui.BeginTabBar("StyleEditorTabs"))
{
var style = ImGui.GetStyle();
if (ImGui.BeginTabItem("Variables"))
{
ImGui.BeginChild($"ScrollingVars", ImGuiHelpers.ScaledVector2(0, -32), true, ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.NoBackground);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 5);
ImGui.SliderFloat2("WindowPadding", ref style.WindowPadding, 0.0f, 20.0f, "%.0f");
ImGui.SliderFloat2("FramePadding", ref style.FramePadding, 0.0f, 20.0f, "%.0f");
ImGui.SliderFloat2("CellPadding", ref style.CellPadding, 0.0f, 20.0f, "%.0f");
ImGui.SliderFloat2("ItemSpacing", ref style.ItemSpacing, 0.0f, 20.0f, "%.0f");
ImGui.SliderFloat2("ItemInnerSpacing", ref style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
ImGui.SliderFloat2("TouchExtraPadding", ref style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
ImGui.SliderFloat("IndentSpacing", ref style.IndentSpacing, 0.0f, 30.0f, "%.0f");
ImGui.SliderFloat("ScrollbarSize", ref style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
ImGui.SliderFloat("GrabMinSize", ref style.GrabMinSize, 1.0f, 20.0f, "%.0f");
ImGui.Text("Borders");
ImGui.SliderFloat("WindowBorderSize", ref style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
ImGui.SliderFloat("ChildBorderSize", ref style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
ImGui.SliderFloat("PopupBorderSize", ref style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
ImGui.SliderFloat("FrameBorderSize", ref style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
ImGui.SliderFloat("TabBorderSize", ref style.TabBorderSize, 0.0f, 1.0f, "%.0f");
ImGui.Text("Rounding");
ImGui.SliderFloat("WindowRounding", ref style.WindowRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("ChildRounding", ref style.ChildRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("FrameRounding", ref style.FrameRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("PopupRounding", ref style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("ScrollbarRounding", ref style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("GrabRounding", ref style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("LogSliderDeadzone", ref style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
ImGui.SliderFloat("TabRounding", ref style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui.Text("Alignment");
ImGui.SliderFloat2("WindowTitleAlign", ref style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
var windowMenuButtonPosition = (int)style.WindowMenuButtonPosition + 1;
if (ImGui.Combo("WindowMenuButtonPosition", ref windowMenuButtonPosition, "None\0Left\0Right\0"))
style.WindowMenuButtonPosition = (ImGuiDir)(windowMenuButtonPosition - 1);
ImGui.SliderFloat2("ButtonTextAlign", ref style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
ImGui.SameLine();
ImGuiComponents.HelpMarker("Alignment applies when a button is larger than its text content.");
ImGui.SliderFloat2("SelectableTextAlign", ref style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
ImGui.SameLine();
ImGuiComponents.HelpMarker("Alignment applies when a selectable is larger than its text content.");
ImGui.SliderFloat2("DisplaySafeAreaPadding", ref style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f");
ImGui.SameLine();
ImGuiComponents.HelpMarker(
"Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
ImGui.EndTabItem();
ImGui.EndChild();
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Colors"))
{
ImGui.BeginChild("ScrollingColors", ImGuiHelpers.ScaledVector2(0, -30), true, ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.NoBackground);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 5);
if (ImGui.RadioButton("Opaque", this.alphaFlags == ImGuiColorEditFlags.None))
this.alphaFlags = ImGuiColorEditFlags.None;
ImGui.SameLine();
if (ImGui.RadioButton("Alpha", this.alphaFlags == ImGuiColorEditFlags.AlphaPreview))
this.alphaFlags = ImGuiColorEditFlags.AlphaPreview;
ImGui.SameLine();
if (ImGui.RadioButton("Both", this.alphaFlags == ImGuiColorEditFlags.AlphaPreviewHalf))
this.alphaFlags = ImGuiColorEditFlags.AlphaPreviewHalf;
ImGui.SameLine();
ImGuiComponents.HelpMarker(
"In the color list:\n" +
"Left-click on color square to open color picker,\n" +
"Right-click to open edit options menu.");
foreach (var imGuiCol in Enum.GetValues<ImGuiCol>())
{
if (imGuiCol == ImGuiCol.COUNT)
continue;
ImGui.PushID(imGuiCol.ToString());
ImGui.ColorEdit4("##color", ref style.Colors[(int)imGuiCol], ImGuiColorEditFlags.AlphaBar | this.alphaFlags);
ImGui.SameLine(0.0f, style.ItemInnerSpacing.X);
ImGui.TextUnformatted(imGuiCol.ToString());
ImGui.PopID();
}
ImGui.EndChild();
ImGui.EndTabItem();
}
ImGui.EndTabBar();
}
ImGui.PopItemWidth();
ImGui.Separator();
if (ImGui.Button("Close"))
{
this.IsOpen = false;
}
ImGui.SameLine();
if (ImGui.Button("Save and Close"))
{
config.ChosenStyle = config.SavedStyles[this.currentSel].Name;
var newStyle = StyleModel.Get();
newStyle.Name = config.ChosenStyle;
config.SavedStyles[this.currentSel] = newStyle;
newStyle.Apply();
config.Save();
this.didSave = true;
this.IsOpen = false;
}
if (ImGui.BeginPopupModal(renameModalTitle, ref this.renameModalDrawing, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar))
{
ImGui.Text("Please enter the new name for this style.");
ImGui.Spacing();
ImGui.InputText("###renameModalInput", ref this.renameText, 255);
const float buttonWidth = 120f;
ImGui.SetCursorPosX((ImGui.GetWindowWidth() - buttonWidth) / 2);
if (ImGui.Button("OK", new Vector2(buttonWidth, 40)))
{
config.SavedStyles[this.currentSel].Name = this.renameText;
config.Save();
ImGui.CloseCurrentPopup();
}
ImGui.EndPopup();
}
}
private static string GetRandomName()
{
var data = Service<DataManager>.Get();
var names = data.GetExcelSheet<BNpcName>(ClientLanguage.English);
var rng = new Random();
return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString;
}
}
}

View file

@ -1,17 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Numerics;
using System.Text;
using Dalamud.Utility;
using ImGuiNET;
using Newtonsoft.Json;
namespace Dalamud.Interface.Internal.Windows.StyleEditor
{
/// <summary>
/// Class representing a serializable ImGui style.
/// </summary>
public class StyleModel
{
/// <summary>
/// Initializes a new instance of the <see cref="StyleModel"/> class.
/// </summary>
private StyleModel()
{
this.Colors = new Dictionary<string, Vector4>();
this.Name = "Unknown";
}
/// <summary>
/// Gets the standard Dalamud look.
/// </summary>
public static StyleModel DalamudStandard => new()
{
Name = "Dalamud Standard",
@ -104,6 +117,8 @@ namespace Dalamud.Interface.Internal.Windows.StyleEditor
},
};
#pragma warning disable SA1600
[JsonProperty("name")]
public string Name { get; set; }
@ -188,81 +203,18 @@ namespace Dalamud.Interface.Internal.Windows.StyleEditor
[JsonProperty("aa")]
public Vector2 DisplaySafeAreaPadding { get; set; }
#pragma warning restore SA1600
/// <summary>
/// Gets or sets a dictionary mapping ImGui color names to colors.
/// </summary>
[JsonProperty("col")]
public Dictionary<string, Vector4> Colors { get; set; }
public static void CopyTo(Stream src, Stream dest) {
byte[] bytes = new byte[4096];
int cnt;
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) {
dest.Write(bytes, 0, cnt);
}
}
public static byte[] Zip(string str) {
var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream()) {
using (var gs = new GZipStream(mso, CompressionMode.Compress)) {
//msi.CopyTo(gs);
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
public static string Unzip(byte[] bytes) {
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream()) {
using (var gs = new GZipStream(msi, CompressionMode.Decompress)) {
//gs.CopyTo(mso);
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}
public string ToJsonEncoded() => "DS1" + System.Convert.ToBase64String(Zip(JsonConvert.SerializeObject(this)));
public void Apply()
{
var style = ImGui.GetStyle();
style.Alpha = this.Alpha;
style.WindowPadding = this.WindowPadding;
style.WindowRounding = this.WindowRounding;
style.WindowBorderSize = this.WindowBorderSize;
style.WindowTitleAlign = this.WindowTitleAlign;
style.WindowMenuButtonPosition = this.WindowMenuButtonPosition;
style.ChildRounding = this.ChildRounding;
style.ChildBorderSize = this.ChildBorderSize;
style.PopupRounding = this.PopupRounding;
style.FramePadding = this.FramePadding;
style.FrameRounding = this.FrameRounding;
style.FrameBorderSize = this.FrameBorderSize;
style.ItemSpacing = this.ItemSpacing;
style.ItemInnerSpacing = this.ItemInnerSpacing;
style.CellPadding = this.CellPadding;
style.TouchExtraPadding = this.TouchExtraPadding;
style.IndentSpacing = this.IndentSpacing;
style.ScrollbarSize = this.ScrollbarSize;
style.ScrollbarRounding = this.ScrollbarRounding;
style.GrabMinSize = this.GrabMinSize;
style.GrabRounding = this.GrabRounding;
style.LogSliderDeadzone = this.LogSliderDeadzone;
style.TabRounding = this.TabRounding;
style.TabBorderSize = this.TabBorderSize;
style.ButtonTextAlign = this.ButtonTextAlign;
style.SelectableTextAlign = this.SelectableTextAlign;
style.DisplaySafeAreaPadding = this.DisplaySafeAreaPadding;
}
/// <summary>
/// Get a <see cref="StyleModel"/> instance via ImGui.
/// </summary>
/// <returns>The newly created <see cref="StyleModel"/> instance.</returns>
public static StyleModel Get()
{
var model = new StyleModel();
@ -311,10 +263,67 @@ namespace Dalamud.Interface.Internal.Windows.StyleEditor
return model;
}
public static StyleModel FromJsonEncoded(string data)
/// <summary>
/// Get a <see cref="StyleModel"/> instance from a compressed base64 string.
/// </summary>
/// <param name="data">The string to decode.</param>
/// <returns>A decompressed <see cref="StyleModel"/>.</returns>
public static StyleModel? FromEncoded(string data)
{
var json = Unzip(Convert.FromBase64String(data.Substring(2)));
var json = Util.DecompressString(Convert.FromBase64String(data.Substring(3)));
return JsonConvert.DeserializeObject<StyleModel>(json);
}
/// <summary>
/// Get this <see cref="StyleModel"/> instance as a encoded base64 string.
/// </summary>
/// <returns>The encoded base64 string.</returns>
public string ToEncoded() => "DS1" + Convert.ToBase64String(Util.CompressString(JsonConvert.SerializeObject(this)));
/// <summary>
/// Apply this StyleModel via ImGui.
/// </summary>
public void Apply()
{
var style = ImGui.GetStyle();
style.Alpha = this.Alpha;
style.WindowPadding = this.WindowPadding;
style.WindowRounding = this.WindowRounding;
style.WindowBorderSize = this.WindowBorderSize;
style.WindowTitleAlign = this.WindowTitleAlign;
style.WindowMenuButtonPosition = this.WindowMenuButtonPosition;
style.ChildRounding = this.ChildRounding;
style.ChildBorderSize = this.ChildBorderSize;
style.PopupRounding = this.PopupRounding;
style.FramePadding = this.FramePadding;
style.FrameRounding = this.FrameRounding;
style.FrameBorderSize = this.FrameBorderSize;
style.ItemSpacing = this.ItemSpacing;
style.ItemInnerSpacing = this.ItemInnerSpacing;
style.CellPadding = this.CellPadding;
style.TouchExtraPadding = this.TouchExtraPadding;
style.IndentSpacing = this.IndentSpacing;
style.ScrollbarSize = this.ScrollbarSize;
style.ScrollbarRounding = this.ScrollbarRounding;
style.GrabMinSize = this.GrabMinSize;
style.GrabRounding = this.GrabRounding;
style.LogSliderDeadzone = this.LogSliderDeadzone;
style.TabRounding = this.TabRounding;
style.TabBorderSize = this.TabBorderSize;
style.ButtonTextAlign = this.ButtonTextAlign;
style.SelectableTextAlign = this.SelectableTextAlign;
style.DisplaySafeAreaPadding = this.DisplaySafeAreaPadding;
foreach (var imGuiCol in Enum.GetValues<ImGuiCol>())
{
if (imGuiCol == ImGuiCol.COUNT)
{
continue;
}
style.Colors[(int)imGuiCol] = this.Colors[imGuiCol.ToString()];
}
}
}
}