feat: add autohide after inactivity
After not receiving a message for X seconds (configurable) in the current tab or any tab with unread mode enabled, the chat will be hidden. Focus can be returned with return or slash as usual. Having input focus or hovering the mouse over the chat window "bumps" it every frame. Also fixes a bug that prevented focus from being restored to tabs with input disabled. The chat window will be brought back but the activated event won't be fully processed. Co-authored-by: Auri <me@aurieh.me>
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
Generated
+36
@@ -2417,6 +2417,24 @@ namespace ChatTwo.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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..
|
||||
/// </summary>
|
||||
internal static string Options_HideWhenInactive_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_HideWhenInactive_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Hide when inactive.
|
||||
/// </summary>
|
||||
internal static string Options_HideWhenInactive_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_HideWhenInactive_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Hide {0} when you are not logged in to a character..
|
||||
/// </summary>
|
||||
@@ -2453,6 +2471,24 @@ namespace ChatTwo.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to How long to wait (in seconds) before considering the chat log inactive..
|
||||
/// </summary>
|
||||
internal static string Options_InactivityHideTimeout_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_InactivityHideTimeout_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Inactivity timeout.
|
||||
/// </summary>
|
||||
internal static string Options_InactivityHideTimeout_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_InactivityHideTimeout_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The font {0} will use to display Japanese text..
|
||||
/// </summary>
|
||||
|
||||
@@ -406,6 +406,18 @@
|
||||
<data name="Options_HideWhenUiHidden_Description">
|
||||
<value>Hide {0} when the game UI is hidden.</value>
|
||||
</data>
|
||||
<data name="Options_HideWhenInactive_Name">
|
||||
<value>Hide when inactive</value>
|
||||
</data>
|
||||
<data name="Options_HideWhenInactive_Description">
|
||||
<value>Hide the chat after a configurable period of inactivity. The current tab and any tabs with unread indicators enabled are considered for activity.</value>
|
||||
</data>
|
||||
<data name="Options_InactivityHideTimeout_Name">
|
||||
<value>Inactivity timeout</value>
|
||||
</data>
|
||||
<data name="Options_InactivityHideTimeout_Description">
|
||||
<value>How long to wait (in seconds) before considering the chat log inactive.</value>
|
||||
</data>
|
||||
<data name="Options_KeybindMode_Name">
|
||||
<value>Keybind mode</value>
|
||||
</data>
|
||||
|
||||
@@ -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<World> WorldSheet;
|
||||
private readonly ExcelSheet<LogFilter> LogFilterSheet;
|
||||
private readonly ExcelSheet<TextCommand> 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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user