feat: add system font selection
This commit is contained in:
@@ -48,6 +48,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.5"/>
|
||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0"/>
|
||||
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="3.3.15"/>
|
||||
<PackageReference Include="XivCommon" Version="5.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -55,6 +57,12 @@
|
||||
<EmbeddedResource Include="fonts\NotoSans-Italic.ttf"/>
|
||||
<EmbeddedResource Include="fonts\NotoSans-Regular.ttf"/>
|
||||
<EmbeddedResource Include="fonts\NotoSansJP-Regular.otf"/>
|
||||
<EmbeddedResource Include="fonts\NotoSerif-Italic.ttf"/>
|
||||
<EmbeddedResource Include="fonts\NotoSerif-Regular.ttf"/>
|
||||
<EmbeddedResource Include="fonts\OpenSans-Italic.ttf"/>
|
||||
<EmbeddedResource Include="fonts\OpenSans-Regular.ttf"/>
|
||||
<EmbeddedResource Include="fonts\Roboto-Italic.ttf"/>
|
||||
<EmbeddedResource Include="fonts\Roboto-Regular.ttf"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ChatTwo.Code;
|
||||
using ChatTwo.Resources;
|
||||
using ChatTwo.Ui;
|
||||
using Dalamud.Configuration;
|
||||
|
||||
namespace ChatTwo;
|
||||
@@ -18,7 +19,11 @@ internal class Configuration : IPluginConfiguration {
|
||||
public bool CanMove = true;
|
||||
public bool CanResize = true;
|
||||
public bool ShowTitleBar;
|
||||
|
||||
public float FontSize = 17f;
|
||||
public string GlobalFont = Fonts.GlobalFonts[0].Name;
|
||||
public string JapaneseFont = Fonts.JapaneseFonts[0].Item1;
|
||||
|
||||
public float WindowAlpha = 1f;
|
||||
public Dictionary<ChatType, uint> ChatColours = new();
|
||||
public List<Tab> Tabs = new();
|
||||
@@ -35,6 +40,8 @@ internal class Configuration : IPluginConfiguration {
|
||||
this.CanResize = other.CanResize;
|
||||
this.ShowTitleBar = other.ShowTitleBar;
|
||||
this.FontSize = other.FontSize;
|
||||
this.GlobalFont = other.GlobalFont;
|
||||
this.JapaneseFont = other.JapaneseFont;
|
||||
this.WindowAlpha = other.WindowAlpha;
|
||||
this.ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
|
||||
this.Tabs = other.Tabs.Select(t => t.Clone()).ToList();
|
||||
|
||||
+66
-17
@@ -79,23 +79,7 @@ internal sealed class PluginUi : IDisposable {
|
||||
builder.AddText("←→↑↓《》■※☀★★☆♥♡ヅツッシ☀☁☂℃℉°♀♂♠♣♦♣♧®©™€$£♯♭♪✓√◎◆◇♦■□〇●△▽▼▲‹›≤≥<«“”─\~Œœ");
|
||||
builder.BuildRanges(out this._ranges);
|
||||
|
||||
var regular = this.GetResource("ChatTwo.fonts.NotoSans-Regular.ttf");
|
||||
this._regularFont = (
|
||||
GCHandle.Alloc(regular, GCHandleType.Pinned),
|
||||
regular.Length
|
||||
);
|
||||
|
||||
var italic = this.GetResource("ChatTwo.fonts.NotoSans-Italic.ttf");
|
||||
this._italicFont = (
|
||||
GCHandle.Alloc(italic, GCHandleType.Pinned),
|
||||
italic.Length
|
||||
);
|
||||
|
||||
var jp = this.GetResource("ChatTwo.fonts.NotoSansJP-Regular.otf");
|
||||
this._jpFont = (
|
||||
GCHandle.Alloc(jp, GCHandleType.Pinned),
|
||||
jp.Length
|
||||
);
|
||||
this.SetUpUserFonts();
|
||||
|
||||
var gameSym = File.ReadAllBytes(Path.Combine(this.Plugin.Interface.DalamudAssetDirectory.FullName, "UIRes", "gamesym.ttf"));
|
||||
this._gameSymFont = (
|
||||
@@ -132,6 +116,69 @@ internal sealed class PluginUi : IDisposable {
|
||||
this._fontCfgMerge.Destroy();
|
||||
}
|
||||
|
||||
private void SetUpUserFonts() {
|
||||
FontData? fontData = null;
|
||||
if (this.Plugin.Config.GlobalFont.StartsWith(Fonts.IncludedIndicator)) {
|
||||
var globalFont = Fonts.GlobalFonts.FirstOrDefault(font => font.Name == this.Plugin.Config.GlobalFont);
|
||||
if (globalFont != null) {
|
||||
fontData = new FontData(this.GetResource(globalFont.ResourcePath), this.GetResource(globalFont.ResourcePathItalic));
|
||||
}
|
||||
} else {
|
||||
fontData = Fonts.GetFont(this.Plugin.Config.GlobalFont, true);
|
||||
}
|
||||
|
||||
if (fontData == null) {
|
||||
PluginLog.Warning("global fallback");
|
||||
var globalFont = Fonts.GlobalFonts[0];
|
||||
fontData = new FontData(this.GetResource(globalFont.ResourcePath), this.GetResource(globalFont.ResourcePathItalic));
|
||||
}
|
||||
|
||||
if (this._regularFont.Item1.IsAllocated) {
|
||||
this._regularFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._italicFont.Item1.IsAllocated) {
|
||||
this._italicFont.Item1.Free();
|
||||
}
|
||||
|
||||
this._regularFont = (
|
||||
GCHandle.Alloc(fontData.Regular, GCHandleType.Pinned),
|
||||
fontData.Regular.Length
|
||||
);
|
||||
|
||||
this._italicFont = (
|
||||
GCHandle.Alloc(fontData.Italic, GCHandleType.Pinned),
|
||||
fontData.Italic.Length
|
||||
);
|
||||
|
||||
FontData? jpFontData = null;
|
||||
if (this.Plugin.Config.JapaneseFont.StartsWith(Fonts.IncludedIndicator)) {
|
||||
var jpFont = Fonts.JapaneseFonts.FirstOrDefault(item => item.Item1 == this.Plugin.Config.JapaneseFont);
|
||||
if (jpFont != default) {
|
||||
jpFontData = new FontData(this.GetResource(jpFont.Item2), Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// jpFontData = Fonts.GetFont(this.Plugin.Config.JapaneseFont, false, CharacterSet.SHIFTJIS_CHARSET);
|
||||
// PluginLog.Log($"data.Regular.Length: {jpFontData?.Regular.Length}");
|
||||
// }
|
||||
|
||||
if (jpFontData == null) {
|
||||
PluginLog.Warning("jp fallback");
|
||||
var jpFont = Fonts.JapaneseFonts[0];
|
||||
jpFontData = new FontData(this.GetResource(jpFont.Item2), Array.Empty<byte>());
|
||||
}
|
||||
|
||||
if (this._jpFont.Item1.IsAllocated) {
|
||||
this._jpFont.Item1.Free();
|
||||
}
|
||||
|
||||
this._jpFont = (
|
||||
GCHandle.Alloc(jpFontData.Regular, GCHandleType.Pinned),
|
||||
jpFontData.Regular.Length
|
||||
);
|
||||
}
|
||||
|
||||
private void Draw() {
|
||||
this.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
|
||||
|
||||
@@ -165,6 +212,8 @@ internal sealed class PluginUi : IDisposable {
|
||||
this.RegularFont = null;
|
||||
this.ItalicFont = null;
|
||||
|
||||
this.SetUpUserFonts();
|
||||
|
||||
// load regular noto sans and merge in jp + game icons
|
||||
this.RegularFont = ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._regularFont.Item1.AddrOfPinnedObject(),
|
||||
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
using System.Drawing;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace ChatTwo.Ui;
|
||||
|
||||
internal static class Fonts {
|
||||
internal const string IncludedIndicator = "Chat 2: ";
|
||||
|
||||
internal static readonly Font[] GlobalFonts = {
|
||||
new(
|
||||
$"{IncludedIndicator}Noto Sans",
|
||||
"ChatTwo.fonts.NotoSans-Regular.ttf",
|
||||
"ChatTwo.fonts.NotoSans-Italic.ttf"
|
||||
),
|
||||
new(
|
||||
$"{IncludedIndicator}Noto Serif",
|
||||
"ChatTwo.fonts.NotoSerif-Regular.ttf",
|
||||
"ChatTwo.fonts.NotoSerif-Italic.ttf"
|
||||
),
|
||||
new(
|
||||
$"{IncludedIndicator}Open Sans",
|
||||
"ChatTwo.fonts.OpenSans-Regular.ttf",
|
||||
"ChatTwo.fonts.OpenSans-Italic.ttf"
|
||||
),
|
||||
new(
|
||||
$"{IncludedIndicator}Roboto",
|
||||
"ChatTwo.fonts.Roboto-Regular.ttf",
|
||||
"ChatTwo.fonts.Roboto-Italic.ttf"
|
||||
),
|
||||
};
|
||||
|
||||
internal static readonly (string, string)[] JapaneseFonts = {
|
||||
($"{IncludedIndicator}Noto Sans JP", "ChatTwo.fonts.NotoSansJP-Regular.otf"),
|
||||
// ($"{IncludedIndicator}Noto Serif JP", "ChatTwo.fonts.NotoSerifJP-Regular.otf"),
|
||||
};
|
||||
|
||||
internal static List<string> GetJpFonts() {
|
||||
var fonts = new List<string>();
|
||||
using var g = Graphics.FromImage(new Bitmap(1, 1));
|
||||
foreach (var (lpelfe, _, fontType) in Gdi32.EnumFontFamiliesEx(g.GetHdc(), CharacterSet.SHIFTJIS_CHARSET)) {
|
||||
var name = lpelfe.elfEnumLogfontEx.elfLogFont.lfFaceName;
|
||||
if (name.StartsWith("@")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fonts.Add(name);
|
||||
}
|
||||
|
||||
return fonts;
|
||||
}
|
||||
|
||||
internal static unsafe FontData? GetFont(string name, bool withItalic, CharacterSet charset = CharacterSet.ANSI_CHARSET) {
|
||||
var regularFont = Gdi32.CreateFontIndirect(new LOGFONT {
|
||||
lfFaceName = name,
|
||||
lfItalic = false,
|
||||
lfCharSet = charset,
|
||||
lfOutPrecision = LogFontOutputPrecision.OUT_TT_ONLY_PRECIS,
|
||||
});
|
||||
|
||||
using var g = Graphics.FromImage(new Bitmap(1, 1));
|
||||
var hdc = g.GetHdc();
|
||||
|
||||
byte[]? GetFontData(HGDIOBJ obj) {
|
||||
Gdi32.SelectObject(hdc, obj);
|
||||
var size = Gdi32.GetFontData(hdc, pvBuffer: IntPtr.Zero);
|
||||
var data = new byte[size];
|
||||
fixed (byte* p = data) {
|
||||
var res = Gdi32.GetFontData(hdc, pvBuffer: (IntPtr) p, cjBuffer: size);
|
||||
Gdi32.DeleteObject(obj);
|
||||
if (res == Gdi32.GDI_ERROR) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
var regular = GetFontData(regularFont);
|
||||
var italic = Array.Empty<byte>();
|
||||
if (withItalic) {
|
||||
var italicFont = Gdi32.CreateFontIndirect(new LOGFONT {
|
||||
lfFaceName = name,
|
||||
lfItalic = true,
|
||||
lfCharSet = charset,
|
||||
lfOutPrecision = LogFontOutputPrecision.OUT_TT_ONLY_PRECIS,
|
||||
});
|
||||
|
||||
italic = GetFontData(italicFont);
|
||||
}
|
||||
|
||||
if (regular == null || italic == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FontData(regular, italic);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FontData {
|
||||
internal byte[] Regular { get; }
|
||||
internal byte[] Italic { get; }
|
||||
|
||||
internal FontData(byte[] regular, byte[] italic) {
|
||||
this.Regular = regular;
|
||||
this.Italic = italic;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Font {
|
||||
internal string Name { get; }
|
||||
internal string ResourcePath { get; }
|
||||
internal string ResourcePathItalic { get; }
|
||||
|
||||
internal Font(string name, string resourcePath, string resourcePathItalic) {
|
||||
this.Name = name;
|
||||
this.ResourcePath = resourcePath;
|
||||
this.ResourcePathItalic = resourcePathItalic;
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,8 @@ internal sealed class Settings : IUiComponent {
|
||||
var config = this.Ui.Plugin.Config;
|
||||
|
||||
var hideChatChanged = this.Mutable.HideChat != this.Ui.Plugin.Config.HideChat;
|
||||
var fontChanged = this.Mutable.GlobalFont != this.Ui.Plugin.Config.GlobalFont
|
||||
|| this.Mutable.JapaneseFont != this.Ui.Plugin.Config.JapaneseFont;
|
||||
var fontSizeChanged = Math.Abs(this.Mutable.FontSize - this.Ui.Plugin.Config.FontSize) > 0.001;
|
||||
|
||||
config.UpdateFrom(this.Mutable);
|
||||
@@ -131,7 +133,7 @@ internal sealed class Settings : IUiComponent {
|
||||
|
||||
this.Ui.Plugin.Store.FilterAllTabs(false);
|
||||
|
||||
if (fontSizeChanged) {
|
||||
if (fontChanged || fontSizeChanged) {
|
||||
this.Ui.Plugin.Interface.UiBuilder.RebuildFonts();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using ChatTwo.Resources;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Text;
|
||||
using ChatTwo.Resources;
|
||||
using ChatTwo.Util;
|
||||
using ImGuiNET;
|
||||
|
||||
@@ -6,14 +8,32 @@ namespace ChatTwo.Ui.SettingsTabs;
|
||||
|
||||
internal sealed class Display : ISettingsTab {
|
||||
private Configuration Mutable { get; }
|
||||
private List<FontFamily> Fonts { get; } = new();
|
||||
private List<string> JpFonts { get; set; } = new();
|
||||
|
||||
public string Name => Language.Options_Display_Tab + "###tabs-display";
|
||||
|
||||
internal Display(Configuration mutable) {
|
||||
this.Mutable = mutable;
|
||||
this.UpdateFonts();
|
||||
}
|
||||
|
||||
private void UpdateFonts() {
|
||||
this.Fonts.Clear();
|
||||
|
||||
var fonts = new InstalledFontCollection();
|
||||
foreach (var font in fonts.Families) {
|
||||
this.Fonts.Add(font);
|
||||
}
|
||||
|
||||
this.JpFonts = Ui.Fonts.GetJpFonts();
|
||||
}
|
||||
|
||||
public void Draw() {
|
||||
if (ImGui.IsWindowAppearing()) {
|
||||
this.UpdateFonts();
|
||||
}
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description);
|
||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.HideDuringCutscenes, Language.Options_HideDuringCutscenes_Name, Language.Options_HideDuringCutscenes_Description);
|
||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.NativeItemTooltips, Language.Options_NativeItemTooltips_Name, Language.Options_NativeItemTooltips_Description);
|
||||
@@ -28,6 +48,62 @@ internal sealed class Display : ISettingsTab {
|
||||
|
||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.ShowNoviceNetwork, Language.Options_ShowNoviceNetwork_Name, Language.Options_ShowNoviceNetwork_Description);
|
||||
|
||||
if (ImGui.BeginCombo("Font", this.Mutable.GlobalFont)) {
|
||||
foreach (var font in Ui.Fonts.GlobalFonts) {
|
||||
if (ImGui.Selectable(font.Name, this.Mutable.GlobalFont == font.Name)) {
|
||||
this.Mutable.GlobalFont = font.Name;
|
||||
}
|
||||
|
||||
if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == font.Name) {
|
||||
ImGui.SetScrollHereY(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
foreach (var family in this.Fonts) {
|
||||
if (!family.IsStyleAvailable(FontStyle.Italic)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui.Selectable(family.Name, this.Mutable.GlobalFont == family.Name)) {
|
||||
this.Mutable.GlobalFont = family.Name;
|
||||
}
|
||||
|
||||
if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == family.Name) {
|
||||
ImGui.SetScrollHereY(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (ImGui.BeginCombo("Japanese font", this.Mutable.JapaneseFont)) {
|
||||
foreach (var (name, _) in Ui.Fonts.JapaneseFonts) {
|
||||
if (ImGui.Selectable(name, this.Mutable.JapaneseFont == name)) {
|
||||
this.Mutable.JapaneseFont = name;
|
||||
}
|
||||
|
||||
if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == name) {
|
||||
ImGui.SetScrollHereY(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// ImGui.Separator();
|
||||
//
|
||||
// foreach (var family in this.JpFonts) {
|
||||
// if (ImGui.Selectable(family, this.Mutable.JapaneseFont == family)) {
|
||||
// this.Mutable.JapaneseFont = family;
|
||||
// }
|
||||
//
|
||||
// if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == family) {
|
||||
// ImGui.SetScrollHereY(0.5f);
|
||||
// }
|
||||
// }
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
ImGui.DragFloat(Language.Options_FontSize_Name, ref this.Mutable.FontSize, .0125f, 12f, 36f, $"{this.Mutable.FontSize:N1}");
|
||||
if (ImGui.DragFloat(Language.Options_WindowOpacity_Name, ref this.Mutable.WindowAlpha, .0025f, 0f, 1f, $"{this.Mutable.WindowAlpha * 100f:N2}%%")) {
|
||||
switch (this.Mutable.WindowAlpha) {
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Reference in New Issue
Block a user