using Dalamud.Interface; using Dalamud.Interface.ImGuiFileDialog; using HSUI.Config; using HSUI.Config.Attributes; using HSUI.Helpers; using Dalamud.Bindings.ImGui; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; using Dalamud.Interface.GameFonts; using Dalamud.Logging; using HSUI.Enums; using HSUI.Interface.Bars; namespace HSUI.Interface.GeneralElements { public struct FontData { public string Name; public int Size; public FontData(string name, int size) { Name = name; Size = size; } } [Disableable(false)] [Section("Customization")] [SubSection("Fonts", 0)] public class FontsConfig : PluginConfigObject { public new static FontsConfig DefaultConfig() { return new FontsConfig(); } public string FontsPath = "C:\\"; [JsonIgnore] public string ValidatedFontsPath => ValidatePath(FontsPath); public SortedList Fonts = new SortedList(); public bool SupportChineseCharacters = false; public bool SupportKoreanCharacters = false; public bool SupportCyrillicCharacters = false; [JsonIgnore] public readonly Dictionary GameFontMap = new Dictionary() { {"Axis", "axis-ffxiv"}, {"Jupiter", "jupiter-ffxiv"}, {"JupiterNumeric", "jupiter-numeric-ffxiv"}, {"MiedingerMid", "meidinger-ffxiv"}, {"Meidinger", "meidinger-numberic-ffxiv"}, {"TrumpGothic", "trumpgothic-ffxiv"}, }; [JsonIgnore] public static readonly List DefaultFontsKeys = new List() { "Expressway_18", "Expressway_14", "Expressway_12" }; [JsonIgnore] public static string DefaultBigFontKey => DefaultFontsKeys[0]; [JsonIgnore] public static string DefaultMediumFontKey => DefaultFontsKeys[1]; [JsonIgnore] public static string DefaultSmallFontKey => DefaultFontsKeys[2]; [JsonIgnore] private int _inputFont = 0; [JsonIgnore] private int _inputSize = 23; [JsonIgnore] private string[] _fonts = null!; [JsonIgnore] private string[] _sizes = null!; [JsonIgnore] private int _removingIndex = -1; [JsonIgnore] private int _applyingIndex = -1!; [JsonIgnore] private FileDialogManager _fileDialogManager = new FileDialogManager(); public FontsConfig() { ReloadFonts(); // default fonts foreach (string key in DefaultFontsKeys) { if (!Fonts.ContainsKey(key)) { string[] str = key.Split("_", StringSplitOptions.RemoveEmptyEntries); var defaultFont = new FontData(str[0], int.Parse(str[1])); Fonts.Add(key, defaultFont); } } // sizes _sizes = new string[100]; for (int i = 0; i < _sizes.Length; i++) { _sizes[i] = (i + 1).ToString(); } } private bool IsDefaultFont(string key) { return DefaultFontsKeys.Contains(key); } private string ValidatePath(string path) { if (path.EndsWith("\\") || path.EndsWith("/")) { return path; } return path + "\\"; } private string[] FontsFromPath(string path) { string[] fonts; try { fonts = Directory.GetFiles(path, "*.ttf"); } catch { fonts = new string[0]; } for (int i = 0; i < fonts.Length; i++) { fonts[i] = fonts[i] .Replace(path, "") .Replace(".ttf", "") .Replace(".TTF", ""); } return fonts; } private string[] FontsFromGame() { string[] gameFontArray = Enum.GetNames(typeof(GameFontFamily)).Skip(1).ToArray(); string[] fonts = new string[gameFontArray.Length]; for (int i = 0; i < gameFontArray.Length; i++) { fonts[i] = GameFontMap[gameFontArray[i]]; } return fonts; } private void ReloadFonts() { string defaultFontsPath = ValidatePath(FontsManager.Instance.DefaultFontsPath); string[] defaultFonts = FontsFromPath(defaultFontsPath); string[] gameFonts = FontsFromGame(); string[] userFonts = FontsFromPath(ValidatedFontsPath); _fonts = new string[defaultFonts.Length + gameFonts.Length + userFonts.Length]; defaultFonts.CopyTo(_fonts, 0); gameFonts.CopyTo(_fonts, defaultFonts.Length); userFonts.CopyTo(_fonts, defaultFonts.Length + gameFonts.Length); } private bool AddNewEntry(int font, int size) { if (font < 0 || font > _fonts.Length) { return false; } if (size <= 0 || size > _sizes.Length) { return false; } string fontName = _fonts[font]; string key = fontName + "_" + size.ToString(); if (Fonts.ContainsKey(key)) { return false; } FontData fontData = new FontData(fontName, size); Fonts.Add(key, fontData); FontsManager.Instance.BuildFonts(); return true; } private void SelectFolder() { Action callback = (finished, path) => { if (finished && path.Length > 0) { FontsPath = path; ReloadFonts(); } }; _fileDialogManager.OpenFolderDialog("Select Fonts Folder", callback); } [ManualDraw] public bool Draw(ref bool changed) { ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.BordersInner | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedSame; if (ImGui.BeginChild("Fonts", new Vector2(800, 500), false, ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) { if (_fonts.Length == 0) { ImGuiHelper.Tab(); ImGui.Text("Default font not found in \"%appdata%/Roaming/XIVLauncher/InstalledPlugins/HSUI/Media/Fonts/Expressway.ttf\""); return false; } ImGuiHelper.NewLineAndTab(); if (ImGui.InputText("Path", ref FontsPath, 200, ImGuiInputTextFlags.EnterReturnsTrue)) { changed = true; ReloadFonts(); } ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Folder.ToIconString(), new Vector2(0, 0))) { SelectFolder(); } ImGui.PopFont(); ImGuiHelper.Tab(); ImGui.Combo("Font ##font", ref _inputFont, _fonts, 10); ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button("\uf2f9", new Vector2(0, 0))) { ReloadFonts(); } ImGui.PopFont(); ImGuiHelper.Tab(); ImGui.Combo("Size ##size", ref _inputSize, _sizes, 10); ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(0, 0))) { changed |= AddNewEntry(_inputFont, _inputSize + 1); } ImGui.PopFont(); ImGuiHelper.NewLineAndTab(); if (ImGui.BeginTable("table", 3, flags, new Vector2(326, 300))) { ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 0, 0); ImGui.TableSetupColumn("Size", ImGuiTableColumnFlags.WidthFixed, 0, 1); ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 0, 2); ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); for (int i = 0; i < Fonts.Count; i++) { var key = Fonts.Keys[i]; var fontData = Fonts.Values[i]; ImGui.PushID(i.ToString()); ImGui.TableNextRow(ImGuiTableRowFlags.None); // icon if (ImGui.TableSetColumnIndex(0)) { ImGui.Text(fontData.Name); } // id if (ImGui.TableSetColumnIndex(1)) { ImGui.Text(fontData.Size.ToString()); } // remove if (ImGui.TableSetColumnIndex(2)) { ImGui.PushFont(UiBuilder.IconFont); ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero); ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero); ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero); if (ImGui.Button(FontAwesomeIcon.ArrowAltCircleUp.ToIconString())) { _applyingIndex = i; } if (!IsDefaultFont(key)) { ImGui.SameLine(); if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString())) { _removingIndex = i; } } ImGui.PopFont(); ImGui.PopStyleColor(3); } ImGui.PopID(); } ImGui.EndTable(); } ImGuiHelper.NewLineAndTab(); if (ImGui.Checkbox("Support Chinese", ref SupportChineseCharacters)) { changed = true; FontsManager.Instance.BuildFonts(); } ImGui.SameLine(); if (ImGui.Checkbox("Support Korean", ref SupportKoreanCharacters)) { changed = true; FontsManager.Instance.BuildFonts(); } ImGui.SameLine(); if (ImGui.Checkbox("Support Cyrillic", ref SupportCyrillicCharacters)) { changed = true; FontsManager.Instance.BuildFonts(); } } // apply confirmation if (_applyingIndex >= 0) { string[] lines = new string[] { "Are you sure you want to apply this font", "to all labels using a font with the same size?" }; var (didConfirm, didClose) = ImGuiHelper.DrawConfirmationModal("Apply to all labels?", lines); if (didConfirm) { var (key, font) = Fonts.ElementAt(_applyingIndex); List labelConfigs = ConfigurationManager.Instance.GetObjects(); foreach (LabelConfig label in labelConfigs) { if (label.FontID != null && Fonts.TryGetValue(label.FontID, out FontData value)) { if (font.Size == value.Size) { label.FontID = key; } } } changed = true; ConfigurationManager.Instance.SaveConfigurations(forced: true); } if (didConfirm || didClose) { _applyingIndex = -1; } } // delete confirmation if (_removingIndex >= 0) { string[] lines = new string[] { "Are you sure you want to remove this font?" }; var (didConfirm, didClose) = ImGuiHelper.DrawConfirmationModal("Remove custom font?", lines); if (didConfirm) { Fonts.RemoveAt(_removingIndex); FontsManager.Instance.BuildFonts(); changed = true; } if (didConfirm || didClose) { _removingIndex = -1; } } ImGui.EndChild(); _fileDialogManager.Draw(); return false; } } }