Implement emotes part 2
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.23.5</Version>
|
<Version>1.24.0</Version>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ internal class Configuration : IPluginConfiguration
|
|||||||
public bool KeepInputFocus = true;
|
public bool KeepInputFocus = true;
|
||||||
public int MaxLinesToRender = 10_000;
|
public int MaxLinesToRender = 10_000;
|
||||||
|
|
||||||
|
public bool ShowEmotes = true;
|
||||||
|
public HashSet<string> BlockedEmotes = [];
|
||||||
|
|
||||||
public bool FontsEnabled = true;
|
public bool FontsEnabled = true;
|
||||||
public ExtraGlyphRanges ExtraGlyphRanges = 0;
|
public ExtraGlyphRanges ExtraGlyphRanges = 0;
|
||||||
public float FontSize = 17f;
|
public float FontSize = 17f;
|
||||||
@@ -88,6 +91,8 @@ internal class Configuration : IPluginConfiguration
|
|||||||
PlaySounds = other.PlaySounds;
|
PlaySounds = other.PlaySounds;
|
||||||
KeepInputFocus = other.KeepInputFocus;
|
KeepInputFocus = other.KeepInputFocus;
|
||||||
MaxLinesToRender = other.MaxLinesToRender;
|
MaxLinesToRender = other.MaxLinesToRender;
|
||||||
|
ShowEmotes = other.ShowEmotes;
|
||||||
|
BlockedEmotes = other.BlockedEmotes;
|
||||||
FontsEnabled = other.FontsEnabled;
|
FontsEnabled = other.FontsEnabled;
|
||||||
ExtraGlyphRanges = other.ExtraGlyphRanges;
|
ExtraGlyphRanges = other.ExtraGlyphRanges;
|
||||||
FontSize = other.FontSize;
|
FontSize = other.FontSize;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public static class EmoteCache
|
|||||||
|
|
||||||
private static readonly Dictionary<string, Emote> Cache = new();
|
private static readonly Dictionary<string, Emote> Cache = new();
|
||||||
|
|
||||||
private static readonly string[] EmoteCodeArray = [];
|
public static readonly string[] EmoteCodeArray = [];
|
||||||
|
|
||||||
static EmoteCache()
|
static EmoteCache()
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-2
@@ -72,7 +72,8 @@ internal class Message
|
|||||||
internal Dictionary<Guid, float?> Height { get; } = new();
|
internal Dictionary<Guid, float?> Height { get; } = new();
|
||||||
internal Dictionary<Guid, bool> IsVisible { get; } = new();
|
internal Dictionary<Guid, bool> IsVisible { get; } = new();
|
||||||
|
|
||||||
internal Message(ulong receiver, ulong contentId, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
internal Message(ulong receiver, ulong contentId, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource)
|
||||||
|
{
|
||||||
Receiver = receiver;
|
Receiver = receiver;
|
||||||
ContentId = contentId;
|
ContentId = contentId;
|
||||||
Date = DateTimeOffset.UtcNow;
|
Date = DateTimeOffset.UtcNow;
|
||||||
@@ -191,7 +192,7 @@ internal class Message
|
|||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
foreach (var word in text.Content.Split(" "))
|
foreach (var word in text.Content.Split(" "))
|
||||||
{
|
{
|
||||||
if (EmoteCache.Exists(word))
|
if (Plugin.Config.ShowEmotes && EmoteCache.Exists(word) && !Plugin.Config.BlockedEmotes.Contains(word))
|
||||||
{
|
{
|
||||||
// We add all the previous collected text parts
|
// We add all the previous collected text parts
|
||||||
AddContentAfterURLCheck(builder.ToString(), text, chunk);
|
AddContentAfterURLCheck(builder.ToString(), text, chunk);
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public sealed class PayloadHandler {
|
|||||||
|
|
||||||
internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button)
|
internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button)
|
||||||
{
|
{
|
||||||
if (LogWindow.Plugin.Config.PlaySounds)
|
if (Plugin.Config.PlaySounds)
|
||||||
UIModule.PlaySound(PopupSfx);
|
UIModule.PlaySound(PopupSfx);
|
||||||
|
|
||||||
switch (button)
|
switch (button)
|
||||||
@@ -230,7 +230,7 @@ public sealed class PayloadHandler {
|
|||||||
DoHover(() => HoverStatus(status), hoverSize);
|
DoHover(() => HoverStatus(status), hoverSize);
|
||||||
break;
|
break;
|
||||||
case ItemPayload item:
|
case ItemPayload item:
|
||||||
if (LogWindow.Plugin.Config.NativeItemTooltips)
|
if (Plugin.Config.NativeItemTooltips)
|
||||||
{
|
{
|
||||||
if (!_handleTooltips || _hoveredItem != item.RawItemId)
|
if (!_handleTooltips || _hoveredItem != item.RawItemId)
|
||||||
{
|
{
|
||||||
@@ -302,7 +302,7 @@ public sealed class PayloadHandler {
|
|||||||
|
|
||||||
var x = isLeft ? window.X : LogWindow.LastWindowPos.X - atkSize.X;
|
var x = isLeft ? window.X : LogWindow.LastWindowPos.X - atkSize.X;
|
||||||
var y = Math.Clamp(window.Y - atkSize.Y, 0, float.MaxValue);
|
var y = Math.Clamp(window.Y - atkSize.Y, 0, float.MaxValue);
|
||||||
y -= isTop ? 0 : LogWindow.Plugin.Config.TooltipOffset; // offset to prevent cut-off on the bottom
|
y -= isTop ? 0 : Plugin.Config.TooltipOffset; // offset to prevent cut-off on the bottom
|
||||||
|
|
||||||
atk->SetPosition((short) x, (short) y);
|
atk->SetPosition((short) x, (short) y);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -40,6 +40,8 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
[PluginService] internal static INotificationManager Notification { get; private set; } = null!;
|
[PluginService] internal static INotificationManager Notification { get; private set; } = null!;
|
||||||
[PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
[PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
||||||
|
|
||||||
|
internal static Configuration Config = null!;
|
||||||
|
|
||||||
public readonly WindowSystem WindowSystem = new(PluginName);
|
public readonly WindowSystem WindowSystem = new(PluginName);
|
||||||
public SettingsWindow SettingsWindow { get; }
|
public SettingsWindow SettingsWindow { get; }
|
||||||
public ChatLogWindow ChatLogWindow { get; }
|
public ChatLogWindow ChatLogWindow { get; }
|
||||||
@@ -48,7 +50,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
public DebuggerWindow DebuggerWindow { get; }
|
public DebuggerWindow DebuggerWindow { get; }
|
||||||
internal LegacyMessageImporterWindow LegacyMessageImporterWindow { get; }
|
internal LegacyMessageImporterWindow LegacyMessageImporterWindow { get; }
|
||||||
|
|
||||||
internal Configuration Config { get; }
|
|
||||||
internal Commands Commands { get; }
|
internal Commands Commands { get; }
|
||||||
internal XivCommonBase Common { get; }
|
internal XivCommonBase Common { get; }
|
||||||
internal TextureCache TextureCache { get; }
|
internal TextureCache TextureCache { get; }
|
||||||
|
|||||||
Generated
+45
@@ -1868,6 +1868,33 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Blocked emotes.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_Emote_BlockedEmotes {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_Emote_BlockedEmotes", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Emote.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_Emote_EmoteTable {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_Emote_EmoteTable", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Emotes.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_Emote_Tab {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_Emote_Tab", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Extra glyphs can be added to {0}'s global font by enabling the checkboxes below. This will likely require increasing Dalamud's font atlas size..
|
/// Looks up a localized string similar to Extra glyphs can be added to {0}'s global font by enabling the checkboxes below. This will likely require increasing Dalamud's font atlas size..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2309,6 +2336,24 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Replaces words with their emote version, currently supports BetterTTV.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_ShowEmotes_Desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_ShowEmotes_Desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Show emotes.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_ShowEmotes_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_ShowEmotes_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Show the Novice Network join button next to the settings button if logged in as a mentor..
|
/// Looks up a localized string similar to Show the Novice Network join button next to the settings button if logged in as a mentor..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1018,4 +1018,19 @@
|
|||||||
<data name="Options_KeepInputFocus_Description" xml:space="preserve">
|
<data name="Options_KeepInputFocus_Description" xml:space="preserve">
|
||||||
<value>Keeps the input focus, even if you enter battle or do other actions</value>
|
<value>Keeps the input focus, even if you enter battle or do other actions</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_ShowEmotes_Name" xml:space="preserve">
|
||||||
|
<value>Show emotes</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_ShowEmotes_Desc" xml:space="preserve">
|
||||||
|
<value>Replaces words with their emote version, currently supports BetterTTV</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_Emote_Tab" xml:space="preserve">
|
||||||
|
<value>Emotes</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_Emote_BlockedEmotes" xml:space="preserve">
|
||||||
|
<value>Blocked emotes</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_Emote_EmoteTable" xml:space="preserve">
|
||||||
|
<value>Emote</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class CommandHelpWindow : Window {
|
|||||||
var width = 350;
|
var width = 350;
|
||||||
var scaledWidth = width * ImGuiHelpers.GlobalScale;
|
var scaledWidth = width * ImGuiHelpers.GlobalScale;
|
||||||
var pos = LogWindow.LastWindowPos;
|
var pos = LogWindow.LastWindowPos;
|
||||||
switch (LogWindow.Plugin.Config.CommandHelpSide) {
|
switch (Plugin.Config.CommandHelpSide) {
|
||||||
case CommandHelpSide.Right:
|
case CommandHelpSide.Right:
|
||||||
pos.X += LogWindow.LastWindowSize.X;
|
pos.X += LogWindow.LastWindowSize.X;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -39,15 +39,16 @@ internal class Popout : Window
|
|||||||
|
|
||||||
public override void PreDraw()
|
public override void PreDraw()
|
||||||
{
|
{
|
||||||
if (ChatLogWindow.Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
if (Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
||||||
StyleModel.GetConfiguredStyles()?.FirstOrDefault(style => style.Name == ChatLogWindow.Plugin.Config.ChosenStyle)?.Push();
|
StyleModel.GetConfiguredStyles()?.FirstOrDefault(style => style.Name == Plugin.Config.ChosenStyle)?.Push();
|
||||||
|
|
||||||
Flags = ImGuiWindowFlags.None;
|
Flags = ImGuiWindowFlags.None;
|
||||||
if (!ChatLogWindow.Plugin.Config.ShowPopOutTitleBar)
|
if (!Plugin.Config.ShowPopOutTitleBar)
|
||||||
Flags |= ImGuiWindowFlags.NoTitleBar;
|
Flags |= ImGuiWindowFlags.NoTitleBar;
|
||||||
|
|
||||||
if (!ChatLogWindow.PopOutDocked[Idx]) {
|
if (!ChatLogWindow.PopOutDocked[Idx])
|
||||||
var alpha = Tab.IndependentOpacity ? Tab.Opacity : ChatLogWindow.Plugin.Config.WindowAlpha;
|
{
|
||||||
|
var alpha = Tab.IndependentOpacity ? Tab.Opacity : Plugin.Config.WindowAlpha;
|
||||||
BgAlpha = alpha / 100f;
|
BgAlpha = alpha / 100f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +57,7 @@ internal class Popout : Window
|
|||||||
{
|
{
|
||||||
using var id = ImRaii.PushId($"popout-{Tab.Identifier}");
|
using var id = ImRaii.PushId($"popout-{Tab.Identifier}");
|
||||||
|
|
||||||
if (!ChatLogWindow.Plugin.Config.ShowPopOutTitleBar)
|
if (!Plugin.Config.ShowPopOutTitleBar)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted(Tab.Name);
|
ImGui.TextUnformatted(Tab.Name);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@@ -70,8 +71,8 @@ internal class Popout : Window
|
|||||||
{
|
{
|
||||||
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
||||||
|
|
||||||
if (ChatLogWindow.Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
if (Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
||||||
StyleModel.GetConfiguredStyles()?.FirstOrDefault(style => style.Name == ChatLogWindow.Plugin.Config.ChosenStyle)?.Pop();
|
StyleModel.GetConfiguredStyles()?.FirstOrDefault(style => style.Name == Plugin.Config.ChosenStyle)?.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnClose()
|
public override void OnClose()
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public sealed class SettingsWindow : Window
|
|||||||
{
|
{
|
||||||
new Display(Mutable),
|
new Display(Mutable),
|
||||||
new ChatLog(Plugin, Mutable),
|
new ChatLog(Plugin, Mutable),
|
||||||
|
new Emote(Plugin, Mutable),
|
||||||
new Ui.SettingsTabs.Fonts(Mutable),
|
new Ui.SettingsTabs.Fonts(Mutable),
|
||||||
new ChatColours(Plugin, Mutable),
|
new ChatColours(Plugin, Mutable),
|
||||||
new Tabs(Plugin, Mutable),
|
new Tabs(Plugin, Mutable),
|
||||||
|
|||||||
@@ -49,5 +49,7 @@ internal sealed class Display : ISettingsTab
|
|||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref Mutable.CollapseDuplicateMessages, Language.Options_CollapseDuplicateMessages_Name, Language.Options_CollapseDuplicateMessages_Description);
|
ImGuiUtil.OptionCheckbox(ref Mutable.CollapseDuplicateMessages, Language.Options_CollapseDuplicateMessages_Name, Language.Options_CollapseDuplicateMessages_Description);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using ChatTwo.Resources;
|
||||||
|
using ChatTwo.Util;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace ChatTwo.Ui.SettingsTabs;
|
||||||
|
|
||||||
|
internal sealed class Emote : ISettingsTab
|
||||||
|
{
|
||||||
|
private readonly Plugin Plugin;
|
||||||
|
private Configuration Mutable { get; }
|
||||||
|
|
||||||
|
public string Name => Language.Options_Emote_Tab + "###tabs-emote";
|
||||||
|
|
||||||
|
private static SearchSelector.SelectorPopupOptions WordPopupOptions = null!;
|
||||||
|
|
||||||
|
internal Emote(Plugin plugin, Configuration mutable)
|
||||||
|
{
|
||||||
|
Plugin = plugin;
|
||||||
|
Mutable = mutable;
|
||||||
|
|
||||||
|
WordPopupOptions = new SearchSelector.SelectorPopupOptions
|
||||||
|
{
|
||||||
|
FilteredSheet = EmoteCache.EmoteCodeArray.Where(w => !Mutable.BlockedEmotes.Contains(w))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(bool changed)
|
||||||
|
{
|
||||||
|
ImGui.PushTextWrapPos();
|
||||||
|
|
||||||
|
ImGuiUtil.OptionCheckbox(ref Mutable.ShowEmotes, Language.Options_ShowEmotes_Name, Language.Options_ShowEmotes_Desc);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
var buttonWidth = ImGui.GetContentRegionAvail().X / 3;
|
||||||
|
using (Plugin.FontManager.FontAwesome.Push())
|
||||||
|
ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
|
||||||
|
|
||||||
|
if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
|
||||||
|
Mutable.BlockedEmotes.Add(newWord);
|
||||||
|
|
||||||
|
using var table = ImRaii.Table("##BlockedWords", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner);
|
||||||
|
if (table)
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn(Language.Options_Emote_EmoteTable);
|
||||||
|
ImGui.TableSetupColumn("##Del", 0, 0.07f);
|
||||||
|
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
|
var copiedList = Mutable.BlockedEmotes.ToArray();
|
||||||
|
foreach (var word in copiedList)
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(word);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if (ImGuiUtil.Button($"##{word}Del", FontAwesomeIcon.Trash, !ImGui.GetIO().KeyCtrl))
|
||||||
|
Mutable.BlockedEmotes.Remove(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Text;
|
|||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Style;
|
using Dalamud.Interface.Style;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
@@ -260,6 +261,15 @@ internal static class ImGuiUtil
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool Button(string id, FontAwesomeIcon icon, bool disabled)
|
||||||
|
{
|
||||||
|
var clicked = false;
|
||||||
|
using (ImRaii.Disabled(disabled))
|
||||||
|
clicked = ImGuiComponents.IconButton(id, icon);
|
||||||
|
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool CtrlShiftButton(string label, string tooltip = "")
|
internal static bool CtrlShiftButton(string label, string tooltip = "")
|
||||||
{
|
{
|
||||||
var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
|
var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using ImGuiNET;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace ChatTwo.Util;
|
||||||
|
|
||||||
|
// Modified from: https://github.com/UnknownX7/Hypostasis/blob/master/ImGui/ExcelSheet.cs
|
||||||
|
public static class SearchSelector
|
||||||
|
{
|
||||||
|
private static string[]? FilteredSearchSheet;
|
||||||
|
|
||||||
|
private static string SheetSearchText = null!;
|
||||||
|
private static string PrevSearchId = null!;
|
||||||
|
private static Type PrevSearchType = null!;
|
||||||
|
|
||||||
|
|
||||||
|
public record SelectorOptions
|
||||||
|
{
|
||||||
|
public Func<string, string> FormatRow { get; init; } = row => row.ToString();
|
||||||
|
public Func<string, string, bool>? SearchPredicate { get; init; } = null;
|
||||||
|
public Func<string, bool, bool>? DrawSelectable { get; init; } = null;
|
||||||
|
public IEnumerable<string> FilteredSheet { get; init; } = [];
|
||||||
|
public Vector2? Size { get; init; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SelectorPopupOptions: SelectorOptions
|
||||||
|
{
|
||||||
|
public ImGuiPopupFlags PopupFlags { get; init; } = ImGuiPopupFlags.None;
|
||||||
|
public bool CloseOnSelection { get; init; } = false;
|
||||||
|
public Func<string, bool> IsSelected { get; init; } = _ => false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SearchInput(string id, IEnumerable<string> filteredSheet, Func<string, string, bool> searchPredicate)
|
||||||
|
{
|
||||||
|
if (ImGui.IsWindowAppearing() && ImGui.IsWindowFocused() && !ImGui.IsAnyItemActive())
|
||||||
|
{
|
||||||
|
if (id != PrevSearchId)
|
||||||
|
{
|
||||||
|
if (typeof(string) != PrevSearchType)
|
||||||
|
{
|
||||||
|
SheetSearchText = string.Empty;
|
||||||
|
PrevSearchType = typeof(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredSearchSheet = null;
|
||||||
|
PrevSearchId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetKeyboardFocusHere(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.InputTextWithHint("##ExcelSheetSearch", "Search", ref SheetSearchText, 128, ImGuiInputTextFlags.AutoSelectAll))
|
||||||
|
FilteredSearchSheet = null;
|
||||||
|
|
||||||
|
FilteredSearchSheet ??= filteredSheet.Where(s => searchPredicate(s, SheetSearchText)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SelectorPopup(string id, out string selected, SelectorPopupOptions? options = null, bool close = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
options ??= new SelectorPopupOptions();
|
||||||
|
var sheet = options.FilteredSheet;
|
||||||
|
selected = string.Empty;
|
||||||
|
|
||||||
|
if (close)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGui.SetNextWindowSize(options.Size ?? new Vector2(0, 250 * ImGuiHelpers.GlobalScale));
|
||||||
|
if (!ImGui.BeginPopupContextItem(id, options.PopupFlags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SearchInput(id, sheet, options.SearchPredicate ?? ((row, s) => options.FormatRow(row).Contains(s, StringComparison.CurrentCultureIgnoreCase)));
|
||||||
|
|
||||||
|
ImGui.BeginChild("SearchList", Vector2.Zero, true);
|
||||||
|
|
||||||
|
var ret = false;
|
||||||
|
var drawSelectable = options.DrawSelectable ?? ((row, selected) => ImGui.Selectable(options.FormatRow(row), selected));
|
||||||
|
using (var clipper = new ListClipper(FilteredSearchSheet!.Length))
|
||||||
|
{
|
||||||
|
foreach (var i in clipper.Rows)
|
||||||
|
{
|
||||||
|
var searched = FilteredSearchSheet[i];
|
||||||
|
ImGui.PushID(id);
|
||||||
|
if (!drawSelectable(searched, options.IsSelected(searched))) continue;
|
||||||
|
selected = searched;
|
||||||
|
ret = true;
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImGui issue #273849, children keep popups from closing automatically
|
||||||
|
if (ret && options.CloseOnSelection)
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
|
||||||
|
ImGui.EndChild();
|
||||||
|
ImGui.EndPopup();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe class ListClipper : IEnumerable<(int, int)>, IDisposable
|
||||||
|
{
|
||||||
|
private ImGuiListClipperPtr Clipper;
|
||||||
|
private readonly int CurrentRows;
|
||||||
|
private readonly int CurrentColumns;
|
||||||
|
private readonly bool TwoDimensional;
|
||||||
|
private readonly int ItemRemainder;
|
||||||
|
|
||||||
|
public int FirstRow { get; private set; } = -1;
|
||||||
|
public int CurrentRow { get; private set; }
|
||||||
|
public int DisplayEnd => Clipper.DisplayEnd;
|
||||||
|
|
||||||
|
public IEnumerable<int> Rows
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
while (Clipper.Step()) // Supposedly this calls End()
|
||||||
|
{
|
||||||
|
if (Clipper.ItemsHeight > 0 && FirstRow < 0)
|
||||||
|
FirstRow = (int)(ImGui.GetScrollY() / Clipper.ItemsHeight);
|
||||||
|
|
||||||
|
for (var i = Clipper.DisplayStart; i < Clipper.DisplayEnd; i++)
|
||||||
|
{
|
||||||
|
CurrentRow = i;
|
||||||
|
yield return TwoDimensional ? i : i * CurrentColumns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<int> Columns
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var cols = (ItemRemainder == 0 || CurrentRows != DisplayEnd || CurrentRow != DisplayEnd - 1) ? CurrentColumns : ItemRemainder;
|
||||||
|
for (var j = 0; j < cols; j++)
|
||||||
|
yield return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListClipper(int items, int cols = 1, bool twoD = false, float itemHeight = 0)
|
||||||
|
{
|
||||||
|
TwoDimensional = twoD;
|
||||||
|
CurrentColumns = cols;
|
||||||
|
CurrentRows = TwoDimensional ? items : (int)MathF.Ceiling((float)items / CurrentColumns);
|
||||||
|
ItemRemainder = !TwoDimensional ? items % CurrentColumns : 0;
|
||||||
|
Clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
|
||||||
|
Clipper.Begin(CurrentRows, itemHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<(int, int)> GetEnumerator() => (from i in Rows from j in Columns select (i, j)).GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Clipper.Destroy(); // This also calls End() but I'm calling it anyway just in case
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user