diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs
index f9bf730..2afa0c7 100755
--- a/ChatTwo/Configuration.cs
+++ b/ChatTwo/Configuration.cs
@@ -20,6 +20,8 @@ internal class Configuration : IPluginConfiguration
public bool HideWhenUiHidden = true;
public bool HideInLoadingScreens;
public bool HideInBattle;
+ public bool HideWhenInactive;
+ public int InactivityHideTimeout = 10;
public bool ShowHideButton = true;
public bool NativeItemTooltips = true;
public bool PrettierTimestamps = true;
@@ -78,6 +80,8 @@ internal class Configuration : IPluginConfiguration
HideWhenUiHidden = other.HideWhenUiHidden;
HideInLoadingScreens = other.HideInLoadingScreens;
HideInBattle = other.HideInBattle;
+ HideWhenInactive = other.HideWhenInactive;
+ InactivityHideTimeout = other.InactivityHideTimeout;
ShowHideButton = other.ShowHideButton;
NativeItemTooltips = other.NativeItemTooltips;
PrettierTimestamps = other.PrettierTimestamps;
@@ -168,6 +172,9 @@ internal class Tab
[NonSerialized]
public uint Unread;
+ [NonSerialized]
+ public long LastMessageTime;
+
[NonSerialized]
public MessageList Messages = new();
@@ -192,7 +199,10 @@ internal class Tab
{
Messages.AddPrune(message, MessageManager.MessageDisplayLimit);
if (unread)
+ {
Unread += 1;
+ LastMessageTime = Environment.TickCount64;
+ }
}
internal void Clear()
diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs
index 0eea03f..421784f 100755
--- a/ChatTwo/GameFunctions/Chat.cs
+++ b/ChatTwo/GameFunctions/Chat.cs
@@ -253,9 +253,6 @@ internal sealed unsafe class Chat : IDisposable
LastRefresh = Environment.TickCount64;
}
- if (Plugin.ChatLogWindow is { CurrentTab.InputDisabled: true, IsHidden: false })
- return;
-
// Vanilla text input has focus
if (RaptureAtkModule.Instance()->AtkModule.IsTextInputActive())
return;
diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs
index 022c8ac..606f01a 100755
--- a/ChatTwo/Resources/Language.Designer.cs
+++ b/ChatTwo/Resources/Language.Designer.cs
@@ -2417,6 +2417,24 @@ namespace ChatTwo.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Hide the chat after a configurable period of inactivity. The current tab and any tabs with unread indicators enabled are considered for activity..
+ ///
+ internal static string Options_HideWhenInactive_Description {
+ get {
+ return ResourceManager.GetString("Options_HideWhenInactive_Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Hide when inactive.
+ ///
+ internal static string Options_HideWhenInactive_Name {
+ get {
+ return ResourceManager.GetString("Options_HideWhenInactive_Name", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Hide {0} when you are not logged in to a character..
///
@@ -2453,6 +2471,24 @@ namespace ChatTwo.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to How long to wait (in seconds) before considering the chat log inactive..
+ ///
+ internal static string Options_InactivityHideTimeout_Description {
+ get {
+ return ResourceManager.GetString("Options_InactivityHideTimeout_Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Inactivity timeout.
+ ///
+ internal static string Options_InactivityHideTimeout_Name {
+ get {
+ return ResourceManager.GetString("Options_InactivityHideTimeout_Name", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The font {0} will use to display Japanese text..
///
diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx
index f535927..ec6eb53 100644
--- a/ChatTwo/Resources/Language.resx
+++ b/ChatTwo/Resources/Language.resx
@@ -406,6 +406,18 @@
Hide {0} when the game UI is hidden.
+
+ Hide when inactive
+
+
+ Hide the chat after a configurable period of inactivity. The current tab and any tabs with unread indicators enabled are considered for activity.
+
+
+ Inactivity timeout
+
+
+ How long to wait (in seconds) before considering the chat log inactive.
+
Keybind mode
diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs
index 3a26655..86991d2 100644
--- a/ChatTwo/Ui/ChatLogWindow.cs
+++ b/ChatTwo/Ui/ChatLogWindow.cs
@@ -85,6 +85,9 @@ public sealed class ChatLogWindow : Window
private const uint ChatCloseSfx = 3u;
private bool PlayedClosingSound = true;
+ private long FrameTime; // set every frame
+ private long LastActivityTime = Environment.TickCount64;
+
private readonly ExcelSheet WorldSheet;
private readonly ExcelSheet LogFilterSheet;
private readonly ExcelSheet TextCommandSheet;
@@ -148,6 +151,18 @@ public sealed class ChatLogWindow : Window
private void Activated(ChatActivatedArgs args)
{
Activate = true;
+ PlayedClosingSound = false;
+ if (Plugin.Config.PlaySounds)
+ UIModule.PlaySound(ChatOpenSfx);
+
+ // Don't set the channel or text content when activating a disabled tab.
+ if (CurrentTab?.InputDisabled == true)
+ {
+ // The closing sound would've been immediately played in this case.
+ PlayedClosingSound = true;
+ return;
+ }
+
if (args.AddIfNotPresent != null && !Chat.Contains(args.AddIfNotPresent))
Chat += args.AddIfNotPresent;
@@ -203,10 +218,6 @@ public sealed class ChatLogWindow : Window
if (info.Text != null && Chat.Length == 0)
Chat = info.Text;
-
- PlayedClosingSound = false;
- if (Plugin.Config.PlaySounds)
- UIModule.PlaySound(ChatOpenSfx);
}
private bool IsValidCommand(string command)
@@ -471,7 +482,23 @@ public sealed class ChatLogWindow : Window
public override bool DrawConditions()
{
- return !IsHidden;
+ FrameTime = Environment.TickCount64;
+ if (IsHidden)
+ return false;
+ if (!Plugin.Config.HideWhenInactive || Activate)
+ {
+ LastActivityTime = FrameTime;
+ return true;
+ }
+
+ var currentTab = CurrentTab; // local to avoid calling the getter repeatedly
+ var lastActivityTime = Plugin.Config.Tabs
+ .Where(tab => tab.UnreadMode is not UnreadMode.None || tab == currentTab)
+ .Select(tab => tab.LastMessageTime)
+ .DefaultIfEmpty(0)
+ .Max();
+ lastActivityTime = Math.Max(lastActivityTime, LastActivityTime);
+ return FrameTime - lastActivityTime <= 1000 * Plugin.Config.InactivityHideTimeout;
}
public override void PreDraw()
@@ -485,6 +512,12 @@ public sealed class ChatLogWindow : Window
public override void PostDraw()
{
+ // Set Activate to false after draw to avoid repeatedly trying to focus
+ // the text input in a tab with input disabled. The usual way that
+ // Activate gets disabled is via the text input callback, but that
+ // doesn't get called if the input is disabled.
+ if (CurrentTab?.InputDisabled == true)
+ Activate = false;
if (Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
StyleModel.GetConfiguredStyles()?.FirstOrDefault(style => style.Name == Plugin.Config.ChosenStyle)?.Pop();
}
@@ -740,7 +773,10 @@ public sealed class ChatLogWindow : Window
}
if (ImGui.IsItemActive())
+ {
HandleKeybinds(true);
+ LastActivityTime = FrameTime;
+ }
// Only trigger unfocused if we are currently not calling the auto complete
if (!Activate && !ImGui.IsItemActive() && AutoCompleteInfo == null)
@@ -785,6 +821,9 @@ public sealed class ChatLogWindow : Window
UserHide();
}
+ if (ImGui.IsWindowHovered(ImGuiHoveredFlags.ChildWindows))
+ LastActivityTime = FrameTime;
+
if (!showNovice)
return;
diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs
index 36a0171..e3d773f 100755
--- a/ChatTwo/Ui/SettingsTabs/Display.cs
+++ b/ChatTwo/Ui/SettingsTabs/Display.cs
@@ -1,7 +1,5 @@
using ChatTwo.Resources;
using ChatTwo.Util;
-using Dalamud.Interface.Style;
-using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
namespace ChatTwo.Ui.SettingsTabs;
@@ -39,6 +37,19 @@ internal sealed class Display : ISettingsTab
ImGuiUtil.OptionCheckbox(ref Mutable.HideInBattle, Language.Options_HideInBattle_Name, Language.Options_HideInBattle_Description);
ImGui.Spacing();
+ ImGuiUtil.OptionCheckbox(ref Mutable.HideWhenInactive, Language.Options_HideWhenInactive_Name, Language.Options_HideWhenInactive_Description);
+ ImGui.Spacing();
+
+ if (Mutable.HideWhenInactive)
+ {
+ ImGuiUtil.InputIntVertical(Language.Options_InactivityHideTimeout_Name,
+ Language.Options_InactivityHideTimeout_Description, ref Mutable.InactivityHideTimeout, 1, 10);
+ // Enforce a minimum of 2 seconds to avoid people soft locking
+ // themselves.
+ Mutable.InactivityHideTimeout = Math.Max(2, Mutable.InactivityHideTimeout);
+ ImGui.Spacing();
+ }
+
ImGuiUtil.OptionCheckbox(ref Mutable.PrettierTimestamps, Language.Options_PrettierTimestamps_Name, Language.Options_PrettierTimestamps_Description);
if (Mutable.PrettierTimestamps)