- net8
- Switch to new font api, - Fix null ref on login - Fix game freeze from chat link handler
This commit is contained in:
+106
-211
@@ -1,8 +1,9 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using ChatTwo.Ui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.Utility;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace ChatTwo;
|
||||
@@ -14,18 +15,18 @@ internal sealed class PluginUi : IDisposable {
|
||||
internal bool ScreenshotMode;
|
||||
internal string Salt { get; }
|
||||
|
||||
internal GameFontHandle? Axis { get; private set; }
|
||||
internal GameFontHandle? AxisItalic { get; private set; }
|
||||
internal IFontHandle Axis { get; private set; }
|
||||
internal IFontHandle AxisItalic { get; private set; }
|
||||
|
||||
internal ImFontPtr? RegularFont { get; private set; }
|
||||
internal ImFontPtr? ItalicFont { get; private set; }
|
||||
internal IFontHandle RegularFont { get; private set; }
|
||||
internal IFontHandle? ItalicFont { get; private set; }
|
||||
internal Vector4 DefaultText { get; private set; }
|
||||
|
||||
internal Tab? CurrentTab {
|
||||
get {
|
||||
var i = this._chatLog.LastTab;
|
||||
if (i > -1 && i < this.Plugin.Config.Tabs.Count) {
|
||||
return this.Plugin.Config.Tabs[i];
|
||||
var i = _chatLog.LastTab;
|
||||
if (i > -1 && i < Plugin.Config.Tabs.Count) {
|
||||
return Plugin.Config.Tabs[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -33,141 +34,74 @@ internal sealed class PluginUi : IDisposable {
|
||||
}
|
||||
|
||||
private List<IUiComponent> Components { get; }
|
||||
private ImFontConfigPtr _fontCfg;
|
||||
private ImFontConfigPtr _fontCfgMerge;
|
||||
private (GCHandle, int, float) _regularFont;
|
||||
private (GCHandle, int, float) _italicFont;
|
||||
private (GCHandle, int, float) _jpFont;
|
||||
private (GCHandle, int) _gameSymFont;
|
||||
|
||||
private ImVector _ranges;
|
||||
private ImVector _jpRange;
|
||||
private FaceData _regularFont;
|
||||
private FaceData? _italicFont;
|
||||
private FaceData _jpFont;
|
||||
private FaceData _gameSymFont;
|
||||
|
||||
private GCHandle _symRange = GCHandle.Alloc(
|
||||
new ushort[] {
|
||||
0xE020,
|
||||
0xE0DB,
|
||||
0,
|
||||
},
|
||||
GCHandleType.Pinned
|
||||
);
|
||||
private ushort[] _ranges;
|
||||
private ushort[] _jpRange;
|
||||
private ushort[] _symRange = [0xE020, 0xE0DB, 0];
|
||||
|
||||
private readonly ChatLog _chatLog;
|
||||
|
||||
internal unsafe PluginUi(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
this.Salt = new Random().Next().ToString();
|
||||
internal PluginUi(Plugin plugin) {
|
||||
Plugin = plugin;
|
||||
Salt = new Random().Next().ToString();
|
||||
|
||||
this._chatLog = new ChatLog(this);
|
||||
this.Components = new List<IUiComponent> {
|
||||
_chatLog = new ChatLog(this);
|
||||
Components = new List<IUiComponent> {
|
||||
new Settings(this),
|
||||
this._chatLog,
|
||||
_chatLog,
|
||||
};
|
||||
|
||||
this._fontCfg = new ImFontConfigPtr(ImGuiNative.ImFontConfig_ImFontConfig()) {
|
||||
FontDataOwnedByAtlas = false,
|
||||
};
|
||||
|
||||
this._fontCfgMerge = new ImFontConfigPtr(ImGuiNative.ImFontConfig_ImFontConfig()) {
|
||||
FontDataOwnedByAtlas = false,
|
||||
MergeMode = true,
|
||||
};
|
||||
|
||||
this.SetUpRanges();
|
||||
this.SetUpUserFonts();
|
||||
|
||||
var gameSym = new HttpClient().GetAsync("https://img.finalfantasyxiv.com/lds/pc/global/fonts/FFXIV_Lodestone_SSF.ttf")
|
||||
.Result
|
||||
.Content
|
||||
.ReadAsByteArrayAsync()
|
||||
.Result;
|
||||
this._gameSymFont = (
|
||||
GCHandle.Alloc(gameSym, GCHandleType.Pinned),
|
||||
gameSym.Length
|
||||
);
|
||||
_gameSymFont = new FaceData(gameSym);
|
||||
|
||||
var uiBuilder = this.Plugin.Interface.UiBuilder;
|
||||
uiBuilder.DisableCutsceneUiHide = true;
|
||||
uiBuilder.DisableGposeUiHide = true;
|
||||
|
||||
uiBuilder.BuildFonts += this.BuildFonts;
|
||||
uiBuilder.Draw += this.Draw;
|
||||
|
||||
uiBuilder.RebuildFonts();
|
||||
Plugin.Interface.UiBuilder.DisableCutsceneUiHide = true;
|
||||
Plugin.Interface.UiBuilder.DisableGposeUiHide = true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.Draw -= this.Draw;
|
||||
this.Plugin.Interface.UiBuilder.BuildFonts -= this.BuildFonts;
|
||||
|
||||
foreach (var component in this.Components) {
|
||||
foreach (var component in Components) {
|
||||
component.Dispose();
|
||||
}
|
||||
|
||||
if (this._regularFont.Item1.IsAllocated) {
|
||||
this._regularFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._italicFont.Item1.IsAllocated) {
|
||||
this._italicFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._jpFont.Item1.IsAllocated) {
|
||||
this._jpFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._gameSymFont.Item1.IsAllocated) {
|
||||
this._gameSymFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._symRange.IsAllocated) {
|
||||
this._symRange.Free();
|
||||
}
|
||||
|
||||
this._fontCfg.Destroy();
|
||||
this._fontCfgMerge.Destroy();
|
||||
}
|
||||
|
||||
private void Draw() {
|
||||
if (this.Plugin.Config.DatabaseMigration != Configuration.LatestDbVersion) {
|
||||
public void Draw() {
|
||||
if (Plugin.Config.DatabaseMigration != Configuration.LatestDbVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Plugin.Interface.UiBuilder.DisableUserUiHide = !this.Plugin.Config.HideWhenUiHidden;
|
||||
this.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
|
||||
Plugin.Interface.UiBuilder.DisableUserUiHide = !Plugin.Config.HideWhenUiHidden;
|
||||
DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
|
||||
|
||||
var font = this.RegularFont.HasValue;
|
||||
var pushed = font && this.Plugin.Config.FontsEnabled;
|
||||
var axis = !this.Plugin.Config.FontsEnabled && (this.Axis?.Available ?? false);
|
||||
|
||||
if (pushed) {
|
||||
ImGui.PushFont(this.RegularFont!.Value);
|
||||
} else if (axis) {
|
||||
ImGui.PushFont(this.Axis!.ImFont);
|
||||
}
|
||||
|
||||
foreach (var component in this.Components) {
|
||||
try {
|
||||
component.Draw();
|
||||
} catch (Exception ex) {
|
||||
Plugin.Log.Error(ex, "Error drawing component");
|
||||
using ((Plugin.Config.FontsEnabled ? RegularFont : Axis).Push())
|
||||
{
|
||||
foreach (var component in Components) {
|
||||
try {
|
||||
component.Draw();
|
||||
} catch (Exception ex) {
|
||||
Plugin.Log.Error(ex, "Error drawing component");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pushed || axis) {
|
||||
ImGui.PopFont();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] GetResource(string name) {
|
||||
var stream = this.GetType().Assembly.GetManifestResourceStream(name)!;
|
||||
var stream = GetType().Assembly.GetManifestResourceStream(name)!;
|
||||
var memory = new MemoryStream();
|
||||
stream.CopyTo(memory);
|
||||
return memory.ToArray();
|
||||
}
|
||||
|
||||
private unsafe void SetUpRanges() {
|
||||
ImVector BuildRange(IReadOnlyList<ushort>? chars, params IntPtr[] ranges) {
|
||||
ushort[] BuildRange(IReadOnlyList<ushort>? chars, params IntPtr[] ranges) {
|
||||
var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder());
|
||||
// text
|
||||
foreach (var range in ranges) {
|
||||
@@ -200,12 +134,7 @@ internal sealed class PluginUi : IDisposable {
|
||||
}
|
||||
|
||||
builder.AddChar('⓪');
|
||||
|
||||
var result = new ImVector();
|
||||
builder.BuildRanges(out result);
|
||||
builder.Destroy();
|
||||
|
||||
return result;
|
||||
return builder.BuildRangesToArray();
|
||||
}
|
||||
|
||||
var ranges = new List<IntPtr> {
|
||||
@@ -213,153 +142,119 @@ internal sealed class PluginUi : IDisposable {
|
||||
};
|
||||
|
||||
foreach (var extraRange in Enum.GetValues<ExtraGlyphRanges>()) {
|
||||
if (this.Plugin.Config.ExtraGlyphRanges.HasFlag(extraRange)) {
|
||||
if (Plugin.Config.ExtraGlyphRanges.HasFlag(extraRange)) {
|
||||
ranges.Add(extraRange.Range());
|
||||
}
|
||||
}
|
||||
|
||||
this._ranges = BuildRange(null, ranges.ToArray());
|
||||
this._jpRange = BuildRange(GlyphRangesJapanese.GlyphRanges);
|
||||
_ranges = BuildRange(null, ranges.ToArray());
|
||||
_jpRange = BuildRange(GlyphRangesJapanese.GlyphRanges);
|
||||
}
|
||||
|
||||
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 (Plugin.Config.GlobalFont.StartsWith(Fonts.IncludedIndicator)) {
|
||||
var globalFont = Fonts.GlobalFonts.FirstOrDefault(font => font.Name == Plugin.Config.GlobalFont);
|
||||
if (globalFont != null) {
|
||||
var regular = new FaceData(this.GetResource(globalFont.ResourcePath), 1f);
|
||||
var italic = new FaceData(this.GetResource(globalFont.ResourcePathItalic), 1f);
|
||||
var regular = new FaceData(GetResource(globalFont.ResourcePath));
|
||||
var italic = new FaceData(GetResource(globalFont.ResourcePathItalic));
|
||||
fontData = new FontData(regular, italic);
|
||||
}
|
||||
} else {
|
||||
fontData = Fonts.GetFont(this.Plugin.Config.GlobalFont, true);
|
||||
fontData = Fonts.GetFont(Plugin.Config.GlobalFont, true);
|
||||
}
|
||||
|
||||
if (fontData == null) {
|
||||
this.Plugin.Config.GlobalFont = Fonts.GlobalFonts[0].Name;
|
||||
this.Plugin.SaveConfig();
|
||||
Plugin.Config.GlobalFont = Fonts.GlobalFonts[0].Name;
|
||||
Plugin.SaveConfig();
|
||||
|
||||
var globalFont = Fonts.GlobalFonts[0];
|
||||
var regular = new FaceData(this.GetResource(globalFont.ResourcePath), 1f);
|
||||
var italic = new FaceData(this.GetResource(globalFont.ResourcePathItalic), 1f);
|
||||
var regular = new FaceData(GetResource(globalFont.ResourcePath));
|
||||
var italic = new FaceData(GetResource(globalFont.ResourcePathItalic));
|
||||
fontData = new FontData(regular, italic);
|
||||
}
|
||||
|
||||
if (this._regularFont.Item1.IsAllocated) {
|
||||
this._regularFont.Item1.Free();
|
||||
}
|
||||
|
||||
if (this._italicFont.Item1.IsAllocated) {
|
||||
this._italicFont.Item1.Free();
|
||||
}
|
||||
|
||||
this._regularFont = (
|
||||
GCHandle.Alloc(fontData.Regular.Data, GCHandleType.Pinned),
|
||||
fontData.Regular.Data.Length,
|
||||
fontData.Regular.Ratio
|
||||
);
|
||||
|
||||
this._italicFont = (
|
||||
GCHandle.Alloc(fontData.Italic!.Data, GCHandleType.Pinned),
|
||||
fontData.Italic.Data.Length,
|
||||
fontData.Italic.Ratio
|
||||
);
|
||||
_regularFont = fontData.Regular;
|
||||
_italicFont = fontData.Italic ?? null;
|
||||
|
||||
FontData? jpFontData = null;
|
||||
if (this.Plugin.Config.JapaneseFont.StartsWith(Fonts.IncludedIndicator)) {
|
||||
var jpFont = Fonts.JapaneseFonts.FirstOrDefault(item => item.Item1 == this.Plugin.Config.JapaneseFont);
|
||||
if (Plugin.Config.JapaneseFont.StartsWith(Fonts.IncludedIndicator)) {
|
||||
var jpFont = Fonts.JapaneseFonts.FirstOrDefault(item => item.Item1 == Plugin.Config.JapaneseFont);
|
||||
if (jpFont != default) {
|
||||
jpFontData = new FontData(
|
||||
new FaceData(this.GetResource(jpFont.Item2), 1f),
|
||||
new FaceData(GetResource(jpFont.Item2)),
|
||||
null
|
||||
);
|
||||
}
|
||||
} else {
|
||||
jpFontData = Fonts.GetFont(this.Plugin.Config.JapaneseFont, false);
|
||||
jpFontData = Fonts.GetFont(Plugin.Config.JapaneseFont, false);
|
||||
}
|
||||
|
||||
if (jpFontData == null) {
|
||||
this.Plugin.Config.JapaneseFont = Fonts.JapaneseFonts[0].Item1;
|
||||
this.Plugin.SaveConfig();
|
||||
Plugin.Config.JapaneseFont = Fonts.JapaneseFonts[0].Item1;
|
||||
Plugin.SaveConfig();
|
||||
|
||||
var jpFont = Fonts.JapaneseFonts[0];
|
||||
jpFontData = new FontData(
|
||||
new FaceData(this.GetResource(jpFont.Item2), 1f),
|
||||
new FaceData(GetResource(jpFont.Item2)),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (this._jpFont.Item1.IsAllocated) {
|
||||
this._jpFont.Item1.Free();
|
||||
}
|
||||
|
||||
this._jpFont = (
|
||||
GCHandle.Alloc(jpFontData.Regular.Data, GCHandleType.Pinned),
|
||||
jpFontData.Regular.Data.Length,
|
||||
jpFontData.Regular.Ratio
|
||||
);
|
||||
_jpFont = jpFontData.Regular;
|
||||
}
|
||||
|
||||
private void BuildFonts() {
|
||||
this.RegularFont = null;
|
||||
this.ItalicFont = null;
|
||||
public void BuildFonts() {
|
||||
SetUpRanges();
|
||||
SetUpUserFonts();
|
||||
|
||||
this.SetUpRanges();
|
||||
this.SetUpUserFonts();
|
||||
|
||||
this.Axis = this.Plugin.Interface.UiBuilder.GetGameFontHandle(new GameFontStyle(GameFontFamily.Axis, this.Plugin.Config.FontSize));
|
||||
this.AxisItalic = this.Plugin.Interface.UiBuilder.GetGameFontHandle(new GameFontStyle(GameFontFamily.Axis, this.Plugin.Config.FontSize) {
|
||||
SkewStrength = this.Plugin.Config.FontSize / 6,
|
||||
Axis = Plugin.Interface.UiBuilder.FontAtlas.NewGameFontHandle(new GameFontStyle(GameFontFamily.Axis, Plugin.Config.FontSize));
|
||||
AxisItalic = Plugin.Interface.UiBuilder.FontAtlas.NewGameFontHandle(new GameFontStyle(GameFontFamily.Axis, Plugin.Config.FontSize)
|
||||
{
|
||||
SkewStrength = Plugin.Config.FontSize / 6
|
||||
});
|
||||
|
||||
// load regular noto sans and merge in jp + game icons
|
||||
this.RegularFont = ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._regularFont.Item1.AddrOfPinnedObject(),
|
||||
this._regularFont.Item2,
|
||||
this.Plugin.Config.FontSize,
|
||||
this._fontCfg,
|
||||
this._ranges.Data
|
||||
);
|
||||
RegularFont = Plugin.Interface.UiBuilder.FontAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(
|
||||
tk =>
|
||||
{
|
||||
var config = new SafeFontConfig { SizePx = Plugin.Config.FontSize, GlyphRanges = _ranges };
|
||||
config.MergeFont = tk.AddFontFromMemory(_regularFont.Data, config, "ChatTwo2 RegularFont");
|
||||
|
||||
ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._jpFont.Item1.AddrOfPinnedObject(),
|
||||
this._jpFont.Item2,
|
||||
this.Plugin.Config.JapaneseFontSize,
|
||||
this._fontCfgMerge,
|
||||
this._jpRange.Data
|
||||
);
|
||||
config.SizePx = Plugin.Config.JapaneseFontSize;
|
||||
config.GlyphRanges = _jpRange;
|
||||
tk.AddFontFromMemory(_jpFont.Data, config, "ChatTwo2 JP Regular");
|
||||
|
||||
ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._gameSymFont.Item1.AddrOfPinnedObject(),
|
||||
this._gameSymFont.Item2,
|
||||
this.Plugin.Config.SymbolsFontSize,
|
||||
this._fontCfgMerge,
|
||||
this._symRange.AddrOfPinnedObject()
|
||||
);
|
||||
config.SizePx = Plugin.Config.SymbolsFontSize;
|
||||
config.GlyphRanges = _symRange;
|
||||
tk.AddFontFromMemory(_gameSymFont.Data, config, "ChatTwo2 Sym Font");
|
||||
|
||||
tk.Font = config.MergeFont;
|
||||
}
|
||||
));
|
||||
|
||||
// load italic noto sans and merge in jp + game icons
|
||||
this.ItalicFont = ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._italicFont.Item1.AddrOfPinnedObject(),
|
||||
this._italicFont.Item2,
|
||||
this.Plugin.Config.FontSize,
|
||||
this._fontCfg,
|
||||
this._ranges.Data
|
||||
);
|
||||
ItalicFont = null;
|
||||
if (_italicFont != null)
|
||||
{
|
||||
ItalicFont = Plugin.Interface.UiBuilder.FontAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(
|
||||
tk =>
|
||||
{
|
||||
var config = new SafeFontConfig { SizePx = Plugin.Config.FontSize, GlyphRanges = _ranges };
|
||||
config.MergeFont = tk.AddFontFromMemory(_italicFont.Data, config, "ChatTwo2 ItalicFont");
|
||||
|
||||
ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._jpFont.Item1.AddrOfPinnedObject(),
|
||||
this._jpFont.Item2,
|
||||
this.Plugin.Config.JapaneseFontSize,
|
||||
this._fontCfgMerge,
|
||||
this._jpRange.Data
|
||||
);
|
||||
config.SizePx = Plugin.Config.JapaneseFontSize;
|
||||
config.GlyphRanges = _jpRange;
|
||||
tk.AddFontFromMemory(_jpFont.Data, config, "ChatTwo2 JP Regular");
|
||||
|
||||
ImGui.GetIO().Fonts.AddFontFromMemoryTTF(
|
||||
this._gameSymFont.Item1.AddrOfPinnedObject(),
|
||||
this._gameSymFont.Item2,
|
||||
this.Plugin.Config.SymbolsFontSize,
|
||||
this._fontCfgMerge,
|
||||
this._symRange.AddrOfPinnedObject()
|
||||
);
|
||||
config.SizePx = Plugin.Config.SymbolsFontSize;
|
||||
config.GlyphRanges = _symRange;
|
||||
tk.AddFontFromMemory(_gameSymFont.Data, config, "ChatTwo2 Sym Font");
|
||||
|
||||
tk.Font = config.MergeFont;
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user