Implement some todos from #27

This commit is contained in:
Infi
2024-04-21 23:16:49 +02:00
parent 926f125cfa
commit 9bdfa58deb
6 changed files with 314 additions and 235 deletions
+54 -19
View File
@@ -84,27 +84,41 @@ internal class LegacyMessageImporterEligibility
} }
} }
internal LegacyMessageImporter StartImport(MessageStore targetStore, bool noLog = false) internal LegacyMessageImporter StartImport(MessageStore targetStore, bool noLog = false, Plugin? plugin = null)
{ {
if (Status != LegacyMessageImporterEligibilityStatus.Eligible) if (Status != LegacyMessageImporterEligibilityStatus.Eligible)
throw new InvalidOperationException($"Migration not eligible: status is {Status}"); throw new InvalidOperationException($"Migration not eligible: status is {Status}");
return new LegacyMessageImporter(targetStore, originalDbPath: OriginalDbPath, migrationDbPath: MigrationDbPath, noLog: noLog); return new LegacyMessageImporter(targetStore, originalDbPath: OriginalDbPath, migrationDbPath: MigrationDbPath, noLog: noLog, plugin);
} }
/// <summary> /// <summary>
/// Makes the migration ineligible so the user won't be asked again. /// Makes the migration ineligible so the user won't be asked again.
/// </summary> /// </summary>
internal void RenameOldDatabase() internal bool RenameOldDatabase()
{ {
File.Move(OriginalDbPath, MigrationDbPath); try
Status = LegacyMessageImporterEligibilityStatus.IneligibleMigrationDbExists; {
AdditionalIneligibilityInfo = "User chose to rename the old database file"; File.Move(OriginalDbPath, MigrationDbPath);
Status = LegacyMessageImporterEligibilityStatus.IneligibleMigrationDbExists;
AdditionalIneligibilityInfo = "User chose to rename the old database file";
return true;
}
catch (Exception ex)
{
Plugin.Log.Error(ex, "Unable to move the old database");
return false;
}
} }
} }
internal class LegacyMessageImporter : IDisposable internal class LegacyMessageImporter : IAsyncDisposable
{ {
private readonly Plugin? Plugin;
private readonly CancellationTokenSource CancellationToken = new();
private Thread? WorkingThread = null;
internal const string MessagesCollection = "messages"; internal const string MessagesCollection = "messages";
private const int MaxFailedMessageLogs = 10; private const int MaxFailedMessageLogs = 10;
@@ -130,16 +144,17 @@ internal class LegacyMessageImporter : IDisposable
// This can be set by the user to limit the rate at which messages are // This can be set by the user to limit the rate at which messages are
// imported. If the rate exceeds this value, the importer will sleep for the // imported. If the rate exceeds this value, the importer will sleep for the
// remainder of the second. // remainder of the second.
internal int MaxMessageRate { get; set; } = 250; // start low internal int MaxMessageRate = 250; // start low
// Do not call this directly, use // Do not call this directly, use
// LegacyMessageImporterEligibility.StartImport instead. // LegacyMessageImporterEligibility.StartImport instead.
internal LegacyMessageImporter(MessageStore targetStore, string? originalDbPath = null, string? migrationDbPath = null, bool noLog = false) internal LegacyMessageImporter(MessageStore targetStore, string? originalDbPath = null, string? migrationDbPath = null, bool noLog = false, Plugin? plugin = null)
{ {
_targetStore = targetStore; _targetStore = targetStore;
originalDbPath ??= Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat.db"); originalDbPath ??= Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat.db");
migrationDbPath ??= migrationDbPath ?? Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-litedb.db"); migrationDbPath ??= migrationDbPath ?? Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-litedb.db");
_log = noLog ? null : Plugin.Log; _log = noLog ? null : Plugin.Log;
Plugin = plugin;
_log?.Info($"[Migration] Moving '{originalDbPath}' to '{migrationDbPath}'"); _log?.Info($"[Migration] Moving '{originalDbPath}' to '{migrationDbPath}'");
File.Move(originalDbPath, migrationDbPath); File.Move(originalDbPath, migrationDbPath);
@@ -147,12 +162,30 @@ internal class LegacyMessageImporter : IDisposable
_database = Connect(migrationDbPath); _database = Connect(migrationDbPath);
ImportStart = Environment.TickCount64; ImportStart = Environment.TickCount64;
new Thread(DoImport).Start(); WorkingThread = new Thread(() => DoImport(CancellationToken.Token));
WorkingThread.Start();
} }
public void Dispose() public void Dispose()
{ {
// TODO: cancel thread and wait for it to close _database?.Dispose();
}
public async ValueTask DisposeAsync()
{
await CancellationToken.CancelAsync();
var timeout = 10_000; // 10s
while (WorkingThread != null && timeout > 0)
{
if (!WorkingThread.IsAlive)
break;
timeout -= 100;
await Task.Delay(100);
Plugin.Log.Information("Sleeping because thread still alive");
}
_database?.Dispose(); _database?.Dispose();
} }
@@ -248,7 +281,7 @@ internal class LegacyMessageImporter : IDisposable
return conn; return conn;
} }
private void DoImport() private void DoImport(CancellationToken token)
{ {
var importRateTimer = Stopwatch.StartNew(); var importRateTimer = Stopwatch.StartNew();
var messagesInLastSecond = 0; var messagesInLastSecond = 0;
@@ -261,6 +294,9 @@ internal class LegacyMessageImporter : IDisposable
var messages = messagesCollection.Query().OrderBy(msg => msg.Date).ToDocuments(); var messages = messagesCollection.Query().OrderBy(msg => msg.Date).ToDocuments();
foreach (var messageDoc in messages) foreach (var messageDoc in messages)
{ {
if (token.IsCancellationRequested)
return;
try try
{ {
var message = BsonDocumentToMessage(messageDoc); var message = BsonDocumentToMessage(messageDoc);
@@ -271,8 +307,7 @@ internal class LegacyMessageImporter : IDisposable
{ {
FailedMessages++; FailedMessages++;
if (FailedMessages <= MaxFailedMessageLogs) if (FailedMessages <= MaxFailedMessageLogs)
_log?.Error( _log?.Error($"[Migration] Failed to import message '{messageDoc["_id"].AsObjectId}' (usually due to corruption): {e}");
$"[Migration] Failed to import message '{messageDoc["_id"].AsObjectId}' (usually due to corruption): {e}");
if (FailedMessages == MaxFailedMessageLogs) if (FailedMessages == MaxFailedMessageLogs)
_log?.Error("[Migration] Further failed message logs will be suppressed"); _log?.Error("[Migration] Further failed message logs will be suppressed");
} }
@@ -293,19 +328,19 @@ internal class LegacyMessageImporter : IDisposable
// Log every 1,000 messages // Log every 1,000 messages
if ((SuccessfulMessages + FailedMessages) % 1000 == 0) if ((SuccessfulMessages + FailedMessages) % 1000 == 0)
_log?.Information( _log?.Information($"[Migration] Progress: successfully imported {SuccessfulMessages}/{totalMessages} messages ({FailedMessages} failures)");
$"[Migration] Progress: successfully imported {SuccessfulMessages}/{totalMessages} messages ({FailedMessages} failures)");
} }
_log?.Information($"[Migration] Imported {SuccessfulMessages}/{FailedMessages} messages, {FailedMessages} failed"); _log?.Information($"[Migration] Imported {SuccessfulMessages}/{FailedMessages} messages, {FailedMessages} failed");
if (ProcessedMessages != totalMessages) if (ProcessedMessages != totalMessages)
_log?.Warning( _log?.Warning($"[Migration] Total message count mismatch: expected {totalMessages}, got {SuccessfulMessages + FailedMessages}");
$"[Migration] Total message count mismatch: expected {totalMessages}, got {SuccessfulMessages + FailedMessages}");
ImportComplete = Environment.TickCount64; ImportComplete = Environment.TickCount64;
_database.Dispose(); _database.Dispose();
_database = null; _database = null;
if (Plugin != null)
Plugin.Framework.Run(() => Plugin.MessageManager.FilterAllTabs(false), token);
} }
private static Message BsonDocumentToMessage(BsonDocument doc) private static Message BsonDocumentToMessage(BsonDocument doc)
+4 -4
View File
@@ -49,7 +49,7 @@ public sealed class Plugin : IDalamudPlugin
public ChatLogWindow ChatLogWindow { get; } public ChatLogWindow ChatLogWindow { get; }
public CommandHelpWindow CommandHelpWindow { get; } public CommandHelpWindow CommandHelpWindow { get; }
public SeStringDebugger SeStringDebugger { get; } public SeStringDebugger SeStringDebugger { get; }
internal LegacyMesasgeImporterWindow LegacyMesasgeImporterWindow { get; } internal LegacyMessageImporterWindow LegacyMessageImporterWindow { get; }
internal Configuration Config { get; } internal Configuration Config { get; }
internal Commands Commands { get; } internal Commands Commands { get; }
@@ -106,8 +106,8 @@ public sealed class Plugin : IDalamudPlugin
MessageManager = new MessageManager(this); // requires Ui MessageManager = new MessageManager(this); // requires Ui
// Requires MessageManager // Requires MessageManager
LegacyMesasgeImporterWindow = new LegacyMesasgeImporterWindow(MessageManager.Store); LegacyMessageImporterWindow = new LegacyMessageImporterWindow(this);
WindowSystem.AddWindow(LegacyMesasgeImporterWindow); WindowSystem.AddWindow(LegacyMessageImporterWindow);
// let all the other components register, then initialise commands // let all the other components register, then initialise commands
Commands.Initialise(); Commands.Initialise();
@@ -143,7 +143,7 @@ public sealed class Plugin : IDalamudPlugin
ChatLogWindow?.Dispose(); ChatLogWindow?.Dispose();
SettingsWindow?.Dispose(); SettingsWindow?.Dispose();
SeStringDebugger?.Dispose(); SeStringDebugger?.Dispose();
LegacyMesasgeImporterWindow?.Dispose(); LegacyMessageImporterWindow?.Dispose();
ExtraChat?.Dispose(); ExtraChat?.Dispose();
Ipc?.Dispose(); Ipc?.Dispose();
-201
View File
@@ -1,201 +0,0 @@
using System.Numerics;
using ChatTwo.Resources;
using ChatTwo.Util;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace ChatTwo.Ui;
internal class LegacyMesasgeImporterWindow : Window
{
private readonly MessageStore _store;
private LegacyMessageImporterEligibility Eligibility { get; set; }
private LegacyMessageImporter? Importer { get; set; }
internal LegacyMesasgeImporterWindow(MessageStore store) : base("Chat 2 Legacy Importer###chat2-legacy-importer")
{
_store = store;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
LogAndNotify();
}
public void Dispose()
{
Importer?.Dispose();
}
private void LogAndNotify()
{
Plugin.Log.Info(
$"[Migration] Checked migration eligibility: {Eligibility.Status} - '{Eligibility.AdditionalIneligibilityInfo}'");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.Eligible:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
Type = NotificationType.Info,
// The user needs to dismiss this for it to go away.
InitialDuration = TimeSpan.FromHours(6),
Title = "Chat 2 Migration",
Content = "Import messages from old database into new database? Click for more information.",
});
// TODO: clicking does not dismiss
notification.Click += _ => IsOpen = true;
break;
}
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
Type = NotificationType.Warning,
InitialDuration = TimeSpan.FromMinutes(1),
Title = "Chat Two Migration",
Content =
"Migration is not possible because the old database could not be opened. Click for more information."
});
// TODO: clicking does not dismiss
notification.Click += _ => IsOpen = true;
break;
}
}
}
public override void Draw()
{
if (Importer != null)
{
DrawImportStatus();
return;
}
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.Eligible)
DrawEligible();
else
DrawIneligible();
}
private void DrawEligible()
{
// TODO: pretty
ImGui.Text("Import database messages from legacy LiteDB database to Sqlite database?");
ImGui.Text($"Message count: {Eligibility.MessageCount}");
ImGui.Text($"Database size: {Eligibility.DatabaseSizeBytes}");
if (ImGui.Button("Yes, import messages"))
{
// Next draw call will run DrawImportStatus().
Importer = Eligibility.StartImport(_store);
return;
}
ImGui.SameLine();
if (ImGuiUtil.CtrlShiftButton("No, do not import messages",
"Ctrl+Shift: renames old database to avoid prompting again"))
{
Eligibility.RenameOldDatabase();
IsOpen = false;
}
}
private void DrawIneligible()
{
// TODO: pretty
ImGui.Text("Your legacy LiteDB database is not eligible for import:");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.IneligibleOriginalDbNotExists:
ImGui.Text("The old database could not be found.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleMigrationDbExists:
ImGui.Text("The migration process was already started.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
ImGui.Text("The old database could not be opened.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleNoMessages:
ImGui.Text("The old database contains no messages.");
break;
case LegacyMessageImporterEligibilityStatus.Eligible:
default:
throw new ArgumentOutOfRangeException();
}
if (!string.IsNullOrWhiteSpace(Eligibility.AdditionalIneligibilityInfo))
ImGui.Text(Eligibility.AdditionalIneligibilityInfo);
// LiteDB failures notify the user, so give them a chance to rename the
// database to avoid prompting again.
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed)
{
if (ImGuiUtil.CtrlShiftButton("Rename old database",
"Ctrl+Shift: rename old database to avoid import prompt in the future"))
{
Eligibility.RenameOldDatabase();
// TODO: notify success as this changes the status
}
}
}
private void DrawImportStatus()
{
// TODO: pretty
if (Importer == null)
return;
var importStart = Importer.ImportStart;
var importEnd = Importer.ImportComplete;
var total = Importer.ImportCount;
var successful = Importer!.SuccessfulMessages;
var failed = Importer.FailedMessages;
var remaining = Importer.RemainingMessages;
if (importEnd != null)
{
ImGui.Text($"Completed migration in {Duration(importStart, importEnd.Value)}");
ImGui.Text($"Successfully imported: {successful} messages");
ImGui.Text($"Failed to import: {failed} messages");
ImGui.Text($"Unaccounted for: {remaining}");
ImGui.Text("See logs for more details: /xllog");
return;
}
// TODO: implement Importer.MaxMessageRate slider in UI, values 0 (infinity) => 10000
ImGui.Text($"Importing messages... {Importer.Progress:P}%");
ImGui.Text($"Duration: {Duration(importStart, Environment.TickCount64)}");
ImGui.Text($"Successfully imported: {successful} messages");
ImGui.Text($"Failed to import: {failed} messages");
ImGui.Text($"Progress: {Importer.ProcessedMessages}/{total} messages");
ImGui.Text($"Remaining: {remaining} messages");
ImGui.Text($"Messages per second: {Importer.CurrentMessageRate}");
ImGui.Text($"Estimated time remaining: {Importer.EstimatedTimeRemaining}");
ImGui.Text("See logs for more details: /xllog");
// TODO: this doesn't render properly
ImGui.ProgressBar(Importer.Progress, new Vector2(0.0f, 0.0f), $"{Importer.Progress:P}%");
if (ImGuiUtil.CtrlShiftButton("Cancel import", "Ctrl+Shift: cancel import and close window"))
{
// TODO: This currently crashes the whole game because we don't ask
// the importer thread to stop and wait for it to stop before
// disposing it.
// See LegacyMessageImporter.Dispose() for more details.
/*
Importer.Dispose();
Importer = null;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
*/
}
}
private static TimeSpan Duration(long startTicks, long endTicks)
{
return endTicks < startTicks ? TimeSpan.Zero : TimeSpan.FromTicks(endTicks - startTicks);
}
}
+225
View File
@@ -0,0 +1,225 @@
using System.Numerics;
using ChatTwo.Util;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.EventArgs;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace ChatTwo.Ui;
internal class LegacyMessageImporterWindow : Window
{
private readonly Plugin Plugin;
private readonly MessageStore _store;
private LegacyMessageImporterEligibility Eligibility { get; set; }
private LegacyMessageImporter? Importer { get; set; }
internal LegacyMessageImporterWindow(Plugin plugin) : base("Chat 2 Legacy Importer###chat2-legacy-importer")
{
Plugin = plugin;
Flags = ImGuiWindowFlags.NoResize;
Size = new Vector2(500, 400);
_store = plugin.MessageManager.Store;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
LogAndNotify();
}
public void Dispose()
{
Importer?.Dispose();
}
private void NotificationClicked(INotificationClickArgs args)
{
IsOpen = true;
args.Notification.DismissNow();
}
private void LogAndNotify()
{
Plugin.Log.Info($"[Migration] Checked migration eligibility: {Eligibility.Status} - '{Eligibility.AdditionalIneligibilityInfo}'");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.Eligible:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
// The user needs to dismiss this for it to go away.
Type = NotificationType.Info,
InitialDuration = TimeSpan.FromHours(24),
Title = "Chat2 Migration",
Content = "Import messages from old database into new database?\nClick for more information.",
Minimized = false,
});
notification.Click += NotificationClicked;
break;
}
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
Type = NotificationType.Warning,
InitialDuration = TimeSpan.FromMinutes(1),
Title = "Chat2 Migration",
Content = "Migration is not possible because the old database could not be opened.\nClick for more information.",
Minimized = false,
});
notification.Click += NotificationClicked;
break;
}
}
}
public override void Draw()
{
if (Importer != null)
{
DrawImportStatus();
return;
}
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.Eligible)
DrawEligible();
else
DrawIneligible();
}
private void DrawEligible()
{
ImGui.TextWrapped("Import database messages from legacy LiteDB database to SQLite database?");
ImGui.Text($"Message count: {Eligibility.MessageCount:N0}");
ImGui.Text($"Database size: {StringUtil.BytesToString(Eligibility.DatabaseSizeBytes)}");
ImGui.Spacing();
var colorNormal = new Vector4(0.0f, 0.70f, 0.0f, 1.0f);
var colorHovered = new Vector4(0.059f, 0.49f, 0.0f, 1.0f);
using (ImRaii.PushColor(ImGuiCol.Button, colorNormal))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, colorHovered))
{
if (ImGui.Button("Yes, import messages"))
{
// Next draw call will run DrawImportStatus().
Importer = Eligibility.StartImport(_store, plugin: Plugin);
return;
}
}
ImGui.SameLine();
if (ImGuiUtil.CtrlShiftButtonColored("No, do not import messages", "Ctrl+Shift: renames old database to avoid prompting again"))
{
Eligibility.RenameOldDatabase();
IsOpen = false;
}
}
private void DrawIneligible()
{
ImGui.Text("Your legacy LiteDB database is not eligible for import:");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.IneligibleOriginalDbNotExists:
ImGui.Text("The old database could not be found.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleMigrationDbExists:
ImGui.Text("The migration process was already started.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
ImGui.Text("The old database could not be opened.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleNoMessages:
ImGui.Text("The old database contains no messages.");
break;
case LegacyMessageImporterEligibilityStatus.Eligible:
default:
throw new ArgumentOutOfRangeException();
}
if (!string.IsNullOrWhiteSpace(Eligibility.AdditionalIneligibilityInfo))
ImGui.Text(Eligibility.AdditionalIneligibilityInfo);
// LiteDB failures notify the user, so give them a chance to rename the
// database to avoid prompting again.
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed)
{
if (ImGuiUtil.CtrlShiftButton("Rename old database", "Ctrl+Shift: rename old database to avoid import prompt in the future"))
{
if (Eligibility.RenameOldDatabase())
WrapperUtil.AddNotification("Successfully renamed the old database.", NotificationType.Success);
else
WrapperUtil.AddNotification("Rename failed, please check /xllog for more information.", NotificationType.Error);
}
}
}
private void DrawImportStatus()
{
if (Importer == null)
return;
if (Importer.ImportComplete != null)
{
ImGui.TextUnformatted($"Completed migration in {Duration(Importer.ImportStart, Importer.ImportComplete.Value):g}");
ImGui.TextUnformatted($"Successfully imported: {Importer.SuccessfulMessages} messages");
ImGui.TextUnformatted($"Failed to import: {Importer.FailedMessages} messages");
ImGui.TextUnformatted($"Unaccounted for: {Importer.RemainingMessages}");
ImGui.TextUnformatted("See logs for more details: /xllog");
ImGui.Spacing();
if (ImGui.Button("Finish"))
IsOpen = false;
return;
}
ImGui.TextUnformatted($"Importing messages ... {Importer.Progress:P}");
ImGuiHelpers.ScaledDummy(10.0f);
ImGui.TextUnformatted($"Duration: {Duration(Importer.ImportStart, Environment.TickCount64):g}");
ImGui.TextUnformatted($"Progress: {Importer.ProcessedMessages}/{Importer.ImportCount} messages ({Importer.FailedMessages} failed)");
ImGuiHelpers.ScaledDummy(10.0f);
var width = ImGui.GetContentRegionAvail().X / 2;
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Import speed:");
ImGui.SameLine();
ImGui.SetNextItemWidth(width);
ImGui.SliderInt("##speedSlider", ref Importer.MaxMessageRate, 1, 10000, "%d m/sec", ImGuiSliderFlags.AlwaysClamp);
ImGui.TextUnformatted($"Current speed: {Importer.CurrentMessageRate:N0} m/sec");
ImGui.TextUnformatted($"Estimated time remaining: {Importer.EstimatedTimeRemaining:g}");
ImGui.TextUnformatted("See logs for more details: /xllog");
ImGuiHelpers.ScaledDummy(10.0f);
ImGui.ProgressBar(Importer.Progress, new Vector2(-1, 0), $"{Importer.Progress:P}");
ImGui.Spacing();
if (ImGuiUtil.CtrlShiftButton("Cancel import", "Ctrl+Shift: cancel import and close window"))
{
Task.Run(async () =>
{
await Importer.DisposeAsync();
Importer = null;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
});
}
}
private static TimeSpan Duration(long startTicks, long endTicks)
{
return endTicks < startTicks ? TimeSpan.Zero : TimeSpan.FromMilliseconds(endTicks - startTicks);
}
}
+2 -2
View File
@@ -108,11 +108,11 @@ internal sealed class Database : ISettingsTab
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Size, StringUtil.BytesToString(DatabaseSize))); ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Size, StringUtil.BytesToString(DatabaseSize)));
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
ImGui.SetTooltip(DatabaseSize.ToString("N0") + "B"); ImGui.SetTooltip(StringUtil.BytesToString(DatabaseSize));
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_LogSize, StringUtil.BytesToString(DatabaseLogSize))); ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_LogSize, StringUtil.BytesToString(DatabaseLogSize)));
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
ImGui.SetTooltip(DatabaseLogSize.ToString("N0") + "B"); ImGui.SetTooltip(StringUtil.BytesToString(DatabaseLogSize));
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_MessageCount, DatabaseMessageCount)); ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_MessageCount, DatabaseMessageCount));
+29 -9
View File
@@ -5,6 +5,7 @@ using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Style; using Dalamud.Interface.Style;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET; using ImGuiNET;
namespace ChatTwo.Util; namespace ChatTwo.Util;
@@ -262,20 +263,39 @@ internal static class ImGuiUtil
internal static bool CtrlShiftButton(string label, string tooltip = "") internal static bool CtrlShiftButton(string label, string tooltip = "")
{ {
var io = ImGui.GetIO(); var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
var ctrlShiftHeld = io.KeyCtrl && io.KeyShift; if (!ctrlShiftHeld)
if (!ctrlShiftHeld) ImGui.BeginDisabled(); ImGui.BeginDisabled();
var ret = ImGui.Button(label) && ctrlShiftHeld; var ret = ImGui.Button(label) && ctrlShiftHeld;
if (!ctrlShiftHeld) ImGui.EndDisabled();
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { if (!ctrlShiftHeld)
ImGui.BeginTooltip(); ImGui.EndDisabled();
ImGui.TextUnformatted(tooltip);
ImGui.EndTooltip(); if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
} ImGui.SetTooltip(tooltip);
return ret; return ret;
} }
internal static bool CtrlShiftButtonColored(string label, string tooltip = "")
{
var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
var colorNormal = new Vector4(0.780f, 0.245f, 0.245f, 1.0f);
var colorHovered = new Vector4(0.7f, 0.0f, 0.0f, 1.0f);
using (ImRaii.PushColor(ImGuiCol.Button, colorNormal))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, colorHovered))
{
var ret = ImGui.Button(label) && ctrlShiftHeld;
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip(tooltip);
return ret;
}
}
internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result) internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result)
{ {
result = key switch { result = key switch {