feat: add channel switcher, settings button, better tooltips
This commit is contained in:
parent
00421587d0
commit
b66ccaa1c0
|
@ -1,34 +1,52 @@
|
|||
namespace ChatTwo.Code;
|
||||
|
||||
internal static class InputChannelExt {
|
||||
internal static ChatType ToChatType(this InputChannel input) {
|
||||
return input switch {
|
||||
InputChannel.Tell => ChatType.TellOutgoing,
|
||||
InputChannel.Say => ChatType.Say,
|
||||
InputChannel.Party => ChatType.Party,
|
||||
InputChannel.Alliance => ChatType.Alliance,
|
||||
InputChannel.Yell => ChatType.Yell,
|
||||
InputChannel.Shout => ChatType.Shout,
|
||||
InputChannel.FreeCompany => ChatType.FreeCompany,
|
||||
InputChannel.PvpTeam => ChatType.PvpTeam,
|
||||
InputChannel.NoviceNetwork => ChatType.NoviceNetwork,
|
||||
InputChannel.CrossLinkshell1 => ChatType.CrossLinkshell1,
|
||||
InputChannel.CrossLinkshell2 => ChatType.CrossLinkshell2,
|
||||
InputChannel.CrossLinkshell3 => ChatType.CrossLinkshell3,
|
||||
InputChannel.CrossLinkshell4 => ChatType.CrossLinkshell4,
|
||||
InputChannel.CrossLinkshell5 => ChatType.CrossLinkshell5,
|
||||
InputChannel.CrossLinkshell6 => ChatType.CrossLinkshell6,
|
||||
InputChannel.CrossLinkshell7 => ChatType.CrossLinkshell7,
|
||||
InputChannel.CrossLinkshell8 => ChatType.CrossLinkshell8,
|
||||
InputChannel.Linkshell1 => ChatType.Linkshell1,
|
||||
InputChannel.Linkshell2 => ChatType.Linkshell2,
|
||||
InputChannel.Linkshell3 => ChatType.Linkshell3,
|
||||
InputChannel.Linkshell4 => ChatType.Linkshell4,
|
||||
InputChannel.Linkshell5 => ChatType.Linkshell5,
|
||||
InputChannel.Linkshell6 => ChatType.Linkshell6,
|
||||
InputChannel.Linkshell7 => ChatType.Linkshell7,
|
||||
InputChannel.Linkshell8 => ChatType.Linkshell8,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(input), input, null),
|
||||
};
|
||||
}
|
||||
internal static ChatType ToChatType(this InputChannel input) => input switch {
|
||||
InputChannel.Tell => ChatType.TellOutgoing,
|
||||
InputChannel.Say => ChatType.Say,
|
||||
InputChannel.Party => ChatType.Party,
|
||||
InputChannel.Alliance => ChatType.Alliance,
|
||||
InputChannel.Yell => ChatType.Yell,
|
||||
InputChannel.Shout => ChatType.Shout,
|
||||
InputChannel.FreeCompany => ChatType.FreeCompany,
|
||||
InputChannel.PvpTeam => ChatType.PvpTeam,
|
||||
InputChannel.NoviceNetwork => ChatType.NoviceNetwork,
|
||||
InputChannel.CrossLinkshell1 => ChatType.CrossLinkshell1,
|
||||
InputChannel.CrossLinkshell2 => ChatType.CrossLinkshell2,
|
||||
InputChannel.CrossLinkshell3 => ChatType.CrossLinkshell3,
|
||||
InputChannel.CrossLinkshell4 => ChatType.CrossLinkshell4,
|
||||
InputChannel.CrossLinkshell5 => ChatType.CrossLinkshell5,
|
||||
InputChannel.CrossLinkshell6 => ChatType.CrossLinkshell6,
|
||||
InputChannel.CrossLinkshell7 => ChatType.CrossLinkshell7,
|
||||
InputChannel.CrossLinkshell8 => ChatType.CrossLinkshell8,
|
||||
InputChannel.Linkshell1 => ChatType.Linkshell1,
|
||||
InputChannel.Linkshell2 => ChatType.Linkshell2,
|
||||
InputChannel.Linkshell3 => ChatType.Linkshell3,
|
||||
InputChannel.Linkshell4 => ChatType.Linkshell4,
|
||||
InputChannel.Linkshell5 => ChatType.Linkshell5,
|
||||
InputChannel.Linkshell6 => ChatType.Linkshell6,
|
||||
InputChannel.Linkshell7 => ChatType.Linkshell7,
|
||||
InputChannel.Linkshell8 => ChatType.Linkshell8,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(input), input, null),
|
||||
};
|
||||
|
||||
public static uint LinkshellIndex(this InputChannel channel) => channel switch {
|
||||
InputChannel.Linkshell1 => 0,
|
||||
InputChannel.Linkshell2 => 1,
|
||||
InputChannel.Linkshell3 => 2,
|
||||
InputChannel.Linkshell4 => 3,
|
||||
InputChannel.Linkshell5 => 4,
|
||||
InputChannel.Linkshell6 => 5,
|
||||
InputChannel.Linkshell7 => 6,
|
||||
InputChannel.Linkshell8 => 7,
|
||||
InputChannel.CrossLinkshell1 => 0,
|
||||
InputChannel.CrossLinkshell2 => 1,
|
||||
InputChannel.CrossLinkshell3 => 2,
|
||||
InputChannel.CrossLinkshell4 => 3,
|
||||
InputChannel.CrossLinkshell5 => 4,
|
||||
InputChannel.CrossLinkshell6 => 5,
|
||||
InputChannel.CrossLinkshell7 => 6,
|
||||
InputChannel.CrossLinkshell8 => 7,
|
||||
_ => uint.MaxValue,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
using ChatTwo.Code;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using ChatTwo.Code;
|
||||
using ChatTwo.Util;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Shell;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace ChatTwo;
|
||||
|
@ -13,21 +18,25 @@ internal unsafe class GameFunctions : IDisposable {
|
|||
private static class Signatures {
|
||||
internal const string ChatLogRefresh = "40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA";
|
||||
internal const string ChangeChannelName = "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6";
|
||||
internal const string ChangeChatChannel = "E8 ?? ?? ?? ?? 0F B7 44 37 ??";
|
||||
}
|
||||
|
||||
private delegate byte ChatLogRefreshDelegate(IntPtr log, ushort eventId, AtkValue* value);
|
||||
|
||||
private delegate IntPtr ChangeChannelNameDelegate(IntPtr agent);
|
||||
|
||||
private delegate IntPtr ChangeChatChannelDelegate(RaptureShellModule* shell, int channel, uint linkshellIdx, Utf8String* tellTarget, byte one);
|
||||
|
||||
internal delegate void ChatActivatedEventDelegate(string? input);
|
||||
|
||||
private Plugin Plugin { get; }
|
||||
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; }
|
||||
private Hook<ChangeChannelNameDelegate>? ChangeChannelNameHook { get; }
|
||||
private readonly ChangeChatChannelDelegate? _changeChatChannel;
|
||||
|
||||
internal event ChatActivatedEventDelegate? ChatActivated;
|
||||
|
||||
internal (InputChannel channel, string name) ChatChannel { get; private set; }
|
||||
internal (InputChannel channel, List<Chunk> name) ChatChannel { get; private set; }
|
||||
|
||||
internal GameFunctions(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
@ -42,6 +51,10 @@ internal unsafe class GameFunctions : IDisposable {
|
|||
this.ChangeChannelNameHook.Enable();
|
||||
}
|
||||
|
||||
if (this.Plugin.SigScanner.TryScanText(Signatures.ChangeChatChannel, out var changeChannelPtr)) {
|
||||
this._changeChatChannel = Marshal.GetDelegateForFunctionPointer<ChangeChatChannelDelegate>(changeChannelPtr);
|
||||
}
|
||||
|
||||
this.Plugin.ClientState.Login += this.Login;
|
||||
this.Login(null, null);
|
||||
}
|
||||
|
@ -66,6 +79,23 @@ internal unsafe class GameFunctions : IDisposable {
|
|||
this.ChangeChannelNameDetour((IntPtr) agent);
|
||||
}
|
||||
|
||||
internal void SetChatChannel(InputChannel channel, string? tellTarget = null) {
|
||||
if (this._changeChatChannel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(tellTarget ?? "");
|
||||
var target = new Utf8String();
|
||||
fixed (byte* tellTargetPtr = bytes) {
|
||||
var zero = stackalloc byte[1];
|
||||
zero[0] = 0;
|
||||
|
||||
target.StringPtr = tellTargetPtr == null ? zero : tellTargetPtr;
|
||||
target.StringLength = bytes.Length;
|
||||
this._changeChatChannel(RaptureShellModule.Instance, (int) (channel + 1), channel.LinkshellIndex(), &target, 1);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetAddonInteractable(string name, bool interactable) {
|
||||
var unitManager = AtkStage.GetSingleton()->RaptureAtkUnitManager;
|
||||
|
||||
|
@ -217,7 +247,12 @@ internal unsafe class GameFunctions : IDisposable {
|
|||
return ret;
|
||||
}
|
||||
|
||||
this.ChatChannel = ((InputChannel) channel, name.TextValue.TrimStart('\uE01E').Trim());
|
||||
var nameChunks = ChunkUtil.ToChunks(name, null).ToList();
|
||||
if (nameChunks.Count > 0 && nameChunks[0] is TextChunk text) {
|
||||
text.Content = text.Content.TrimStart('\uE01E').TrimStart();
|
||||
}
|
||||
|
||||
this.ChatChannel = ((InputChannel) channel, nameChunks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|||
using Dalamud.Logging;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
|
||||
namespace ChatTwo;
|
||||
|
||||
|
@ -109,7 +110,20 @@ internal sealed class PayloadHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private static void InlineIcon(TextureWrap icon) {
|
||||
var lineHeight = ImGui.CalcTextSize("A").Y;
|
||||
|
||||
var cursor = ImGui.GetCursorPos();
|
||||
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPos(cursor + new Vector2(icon.Width + 4, (float) icon.Height / 2 - lineHeight / 2));
|
||||
}
|
||||
|
||||
private void HoverStatus(StatusPayload status) {
|
||||
if (this.Ui.Plugin.TextureCache.GetStatus(status.Status) is { } icon) {
|
||||
InlineIcon(icon);
|
||||
}
|
||||
|
||||
var name = ChunkUtil.ToChunks(status.Status.Name.ToDalamudString(), null);
|
||||
this.Log.DrawChunks(name.ToList());
|
||||
ImGui.Separator();
|
||||
|
@ -119,6 +133,10 @@ internal sealed class PayloadHandler {
|
|||
}
|
||||
|
||||
private void HoverItem(ItemPayload item) {
|
||||
if (this.Ui.Plugin.TextureCache.GetItem(item.Item) is { } icon) {
|
||||
InlineIcon(icon);
|
||||
}
|
||||
|
||||
var name = ChunkUtil.ToChunks(item.Item.Name.ToDalamudString(), null);
|
||||
this.Log.DrawChunks(name.ToList());
|
||||
ImGui.Separator();
|
||||
|
|
|
@ -46,6 +46,7 @@ public sealed class Plugin : IDalamudPlugin {
|
|||
|
||||
internal Configuration Config { get; }
|
||||
internal XivCommonBase Common { get; }
|
||||
internal TextureCache TextureCache { get; }
|
||||
internal GameFunctions Functions { get; }
|
||||
internal Store Store { get; }
|
||||
internal PluginUi Ui { get; }
|
||||
|
@ -54,6 +55,7 @@ public sealed class Plugin : IDalamudPlugin {
|
|||
public Plugin() {
|
||||
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
this.Common = new XivCommonBase();
|
||||
this.TextureCache = new TextureCache(this.DataManager!);
|
||||
this.Functions = new GameFunctions(this);
|
||||
this.Store = new Store(this);
|
||||
this.Ui = new PluginUi(this);
|
||||
|
@ -69,6 +71,7 @@ public sealed class Plugin : IDalamudPlugin {
|
|||
this.Ui.Dispose();
|
||||
this.Store.Dispose();
|
||||
this.Functions.Dispose();
|
||||
this.TextureCache.Dispose();
|
||||
this.Common.Dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ namespace ChatTwo;
|
|||
|
||||
internal sealed class PluginUi : IDisposable {
|
||||
internal Plugin Plugin { get; }
|
||||
|
||||
internal bool SettingsVisible;
|
||||
|
||||
internal ImFontPtr? RegularFont { get; private set; }
|
||||
internal ImFontPtr? ItalicFont { get; private set; }
|
||||
internal Vector4 DefaultText { get; private set; }
|
||||
|
@ -55,7 +58,7 @@ internal sealed class PluginUi : IDisposable {
|
|||
|
||||
var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder());
|
||||
builder.AddRanges(ImGui.GetIO().Fonts.GetGlyphRangesDefault());
|
||||
builder.AddText("←→↑↓《》■※☀★★☆♥♡ヅツッシ☀☁☂℃℉°♀♂♠♣♦♣♧®©™€$£♯♭♪✓√◎◆◇♦■□〇●△▽▼▲‹›≤≥<«“”─");
|
||||
builder.AddText("←→↑↓《》■※☀★★☆♥♡ヅツッシ☀☁☂℃℉°♀♂♠♣♦♣♧®©™€$£♯♭♪✓√◎◆◇♦■□〇●△▽▼▲‹›≤≥<«“”─\~");
|
||||
builder.BuildRanges(out this._ranges);
|
||||
|
||||
var regular = this.GetResource("ChatTwo.fonts.NotoSans-Regular.ttf");
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
using Dalamud.Data;
|
||||
using ImGuiScene;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace ChatTwo;
|
||||
|
||||
internal class TextureCache : IDisposable {
|
||||
private DataManager Data { get; }
|
||||
|
||||
private readonly Dictionary<uint, TextureWrap> _itemIcons = new();
|
||||
private readonly Dictionary<uint, TextureWrap> _statusIcons = new();
|
||||
|
||||
internal IReadOnlyDictionary<uint, TextureWrap> ItemIcons => this._itemIcons;
|
||||
internal IReadOnlyDictionary<uint, TextureWrap> StatusIcons => this._statusIcons;
|
||||
|
||||
internal TextureCache(DataManager data) {
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
var allIcons = this.ItemIcons.Values
|
||||
.Concat(this.StatusIcons.Values);
|
||||
|
||||
foreach (var tex in allIcons) {
|
||||
tex.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddIcon(IDictionary<uint, TextureWrap> dict, uint icon) {
|
||||
if (dict.ContainsKey(icon)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tex = this.Data.GetImGuiTextureIcon(icon);
|
||||
if (tex != null) {
|
||||
dict[icon] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddItem(Item item) {
|
||||
this.AddIcon(this._itemIcons, item.Icon);
|
||||
}
|
||||
|
||||
internal void AddStatus(Status status) {
|
||||
this.AddIcon(this._statusIcons, status.Icon);
|
||||
}
|
||||
|
||||
internal TextureWrap? GetItem(Item item) {
|
||||
this.AddItem(item);
|
||||
this.ItemIcons.TryGetValue(item.Icon, out var icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
internal TextureWrap? GetStatus(Status status) {
|
||||
this.AddStatus(status);
|
||||
this.StatusIcons.TryGetValue(status.Icon, out var icon);
|
||||
return icon;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
using System.Numerics;
|
||||
using ChatTwo.Code;
|
||||
using ChatTwo.Util;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace ChatTwo.Ui;
|
||||
|
||||
internal sealed class ChatLog : IUiComponent {
|
||||
private const string ChatChannelPicker = "chat-channel-picker";
|
||||
|
||||
private PluginUi Ui { get; }
|
||||
|
||||
internal bool Activate;
|
||||
|
@ -143,7 +147,39 @@ internal sealed class ChatLog : IUiComponent {
|
|||
ImGui.SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted(this.Ui.Plugin.Functions.ChatChannel.name);
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
try {
|
||||
this.DrawChunks(this.Ui.Plugin.Functions.ChatChannel.name);
|
||||
} finally {
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
var beforeIcon = ImGui.GetCursorPos();
|
||||
|
||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment)) {
|
||||
ImGui.OpenPopup(ChatChannelPicker);
|
||||
}
|
||||
|
||||
if (ImGui.BeginPopup(ChatChannelPicker)) {
|
||||
foreach (var channel in Enum.GetValues<InputChannel>()) {
|
||||
var name = this.Ui.Plugin.DataManager.GetExcelSheet<LogFilter>()!
|
||||
.FirstOrDefault(row => row.LogKind == (byte) channel.ToChatType())
|
||||
?.Name
|
||||
?.RawString ?? channel.ToString();
|
||||
|
||||
if (ImGui.Selectable(name)) {
|
||||
this.Ui.Plugin.Functions.SetChatChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var afterIcon = ImGui.GetCursorPos();
|
||||
|
||||
var buttonWidth = afterIcon.X - beforeIcon.X;
|
||||
var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth;
|
||||
|
||||
var inputType = this.Ui.Plugin.Functions.ChatChannel.channel.ToChatType();
|
||||
var inputColour = this.Ui.Plugin.Config.ChatColours.TryGetValue(inputType, out var inputCol)
|
||||
|
@ -154,7 +190,7 @@ internal sealed class ChatLog : IUiComponent {
|
|||
ImGui.PushStyleColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(inputColour.Value));
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
ImGui.SetNextItemWidth(inputWidth);
|
||||
const ImGuiInputTextFlags inputFlags = ImGuiInputTextFlags.EnterReturnsTrue
|
||||
| ImGuiInputTextFlags.CallbackAlways
|
||||
| ImGuiInputTextFlags.CallbackHistory;
|
||||
|
@ -174,6 +210,12 @@ internal sealed class ChatLog : IUiComponent {
|
|||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Cog)) {
|
||||
this.Ui.SettingsVisible ^= true;
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ namespace ChatTwo.Ui;
|
|||
internal sealed class Settings : IUiComponent {
|
||||
private PluginUi Ui { get; }
|
||||
|
||||
private bool _visible;
|
||||
|
||||
private bool _hideChat;
|
||||
private bool _nativeItemTooltips;
|
||||
private float _fontSize;
|
||||
|
@ -29,7 +27,7 @@ internal sealed class Settings : IUiComponent {
|
|||
}
|
||||
|
||||
private void Command(string command, string args) {
|
||||
this._visible ^= true;
|
||||
this.Ui.SettingsVisible ^= true;
|
||||
}
|
||||
|
||||
private void Initialise() {
|
||||
|
@ -42,11 +40,11 @@ internal sealed class Settings : IUiComponent {
|
|||
}
|
||||
|
||||
public void Draw() {
|
||||
if (!this._visible) {
|
||||
if (!this.Ui.SettingsVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ImGui.Begin($"{this.Ui.Plugin.Name} settings", ref this._visible)) {
|
||||
if (!ImGui.Begin($"{this.Ui.Plugin.Name} settings", ref this.Ui.SettingsVisible)) {
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
@ -148,13 +146,13 @@ internal sealed class Settings : IUiComponent {
|
|||
|
||||
if (ImGui.Button("Save and close")) {
|
||||
save = true;
|
||||
this._visible = false;
|
||||
this.Ui.SettingsVisible = false;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Discard")) {
|
||||
this._visible = false;
|
||||
this.Ui.SettingsVisible = false;
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace ChatTwo.Util;
|
||||
|
@ -34,14 +35,20 @@ internal static class ImGuiUtil {
|
|||
PostPayload(payload, handler);
|
||||
}
|
||||
|
||||
if (csText.Length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var part in csText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)) {
|
||||
var bytes = Encoding.UTF8.GetBytes(part);
|
||||
fixed (byte* rawText = bytes) {
|
||||
var text = rawText;
|
||||
var textEnd = text + bytes.Length;
|
||||
|
||||
// idk how this is possible, but it is, I guess
|
||||
// empty string
|
||||
if (text == null) {
|
||||
ImGui.TextUnformatted("");
|
||||
ImGui.TextUnformatted("");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,6 +70,8 @@ internal static class ImGuiUtil {
|
|||
|
||||
endPrevLine = ImGuiNative.ImFont_CalcWordWrapPositionA(ImGui.GetFont().NativePtr, scale, text, textEnd, widthLeft);
|
||||
if (endPrevLine == null) {
|
||||
ImGui.TextUnformatted("");
|
||||
ImGui.TextUnformatted("");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -71,4 +80,19 @@ internal static class ImGuiUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IconButton(FontAwesomeIcon icon, string? id = null) {
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
|
||||
var label = icon.ToIconString();
|
||||
if (id != null) {
|
||||
label += $"##{id}";
|
||||
}
|
||||
|
||||
var ret = ImGui.Button(label);
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue