c4c85cf4b8
- Translated project documentation (LEARNING-JOURNEY, CONTRIBUTORS, AI_DISCLOSURE) to English for better accessibility. - Standardized internal code documentation by converting XML-doc blocks to standard comment format. - Cleaned up inline comments and removed redundant versioning metadata across the codebase. - Refactored non-functional text elements to improve readability and maintain a consistent style.
269 lines
8.5 KiB
C#
269 lines
8.5 KiB
C#
using System.Numerics;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface.Style;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Interface.Windowing;
|
|
|
|
namespace HellionChat.Ui;
|
|
|
|
internal class Popout : Window
|
|
{
|
|
private readonly ChatLogWindow ChatLogWindow;
|
|
private readonly Tab Tab;
|
|
private readonly int Idx;
|
|
|
|
private long FrameTime;
|
|
private long LastActivityTime = Environment.TickCount64;
|
|
|
|
// Optional input bar inside the pop-out. Lazy-allocated when enabled,
|
|
// torn down on toggle-off (buffer discarded intentionally).
|
|
public ChatInputBar? InputBar { get; private set; }
|
|
public bool HasFocusedInputBar => InputBar?.IsFocused ?? false;
|
|
|
|
// Exposed so AutoTellTabsService can locate this window during LRU eviction.
|
|
internal Guid TabIdentifier => Tab.Identifier;
|
|
|
|
public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx)
|
|
: base($"{tab.Name}##popout")
|
|
{
|
|
ChatLogWindow = chatLogWindow;
|
|
Tab = tab;
|
|
Idx = idx;
|
|
|
|
Size = new Vector2(350, 350);
|
|
SizeCondition = ImGuiCond.FirstUseEver;
|
|
|
|
IsOpen = true;
|
|
RespectCloseHotkey = false;
|
|
DisableWindowSounds = true;
|
|
// AllowBackgroundBlur is intentionally off: Dalamud blurs the entire
|
|
// tab container, not just this window, which would affect adjacent plugins.
|
|
// Users can enable blur per-window via the Dalamud hamburger menu.
|
|
}
|
|
|
|
public override void PreOpenCheck()
|
|
{
|
|
if (!Tab.PopOut)
|
|
IsOpen = false;
|
|
}
|
|
|
|
public override bool DrawConditions()
|
|
{
|
|
FrameTime = Environment.TickCount64;
|
|
if (Tab.IndependentHide ? HideStateCheck() : ChatLogWindow.IsHidden)
|
|
return false;
|
|
|
|
if (
|
|
!Plugin.Config.HideWhenInactive
|
|
|| (!Plugin.Config.InactivityHideActiveDuringBattle && Plugin.InBattle)
|
|
|| !Tab.UnhideOnActivity
|
|
)
|
|
{
|
|
LastActivityTime = FrameTime;
|
|
return true;
|
|
}
|
|
|
|
var lastActivityTime = Math.Max(Tab.LastActivity, LastActivityTime);
|
|
lastActivityTime = Math.Max(lastActivityTime, ChatLogWindow.LastActivityTime);
|
|
return FrameTime - lastActivityTime <= 1000 * Plugin.Config.InactivityHideTimeout;
|
|
}
|
|
|
|
public override void PreDraw()
|
|
{
|
|
// Theme engine pushes the active theme globally in Plugin.Draw;
|
|
// pop-outs draw consistently without per-window overrides.
|
|
Flags = ImGuiWindowFlags.None;
|
|
if (!Plugin.Config.ShowPopOutTitleBar)
|
|
Flags |= ImGuiWindowFlags.NoTitleBar;
|
|
|
|
if (!Tab.CanMove)
|
|
Flags |= ImGuiWindowFlags.NoMove;
|
|
|
|
if (!Tab.CanResize)
|
|
Flags |= ImGuiWindowFlags.NoResize;
|
|
|
|
// Guard against Idx pointing past the end if PopOutDocked was resized mid-frame.
|
|
if (Idx >= 0 && Idx < ChatLogWindow.PopOutDocked.Count && !ChatLogWindow.PopOutDocked[Idx])
|
|
{
|
|
BgAlpha = Tab.IndependentOpacity ? Tab.Opacity / 100f : Plugin.Config.WindowOpacity;
|
|
}
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
using var id = ImRaii.PushId($"popout-{Tab.Identifier}");
|
|
|
|
if (!Plugin.Config.ShowPopOutTitleBar)
|
|
{
|
|
ImGui.TextUnformatted(Tab.Name);
|
|
ImGui.Separator();
|
|
}
|
|
|
|
var hintBannerHeight = DrawHintBannerIfNeeded();
|
|
|
|
// Toggle-OFF resets InputBar so the next toggle-ON starts with a fresh buffer.
|
|
var inputEnabled = Plugin.Config.PopOutInputEnabled;
|
|
if (!inputEnabled && InputBar != null)
|
|
InputBar = null;
|
|
|
|
if (inputEnabled)
|
|
InputBar ??= new ChatInputBar(ChatLogWindow.Plugin, ChatLogWindow, () => Tab);
|
|
|
|
var inputBarHeight = inputEnabled
|
|
? ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().ItemSpacing.Y
|
|
: 0f;
|
|
|
|
var handler = ChatLogWindow.HandlerLender.Borrow();
|
|
var logHeight = ImGui.GetContentRegionAvail().Y - inputBarHeight - hintBannerHeight;
|
|
ChatLogWindow.DrawMessageLog(Tab, handler, logHeight, false);
|
|
|
|
if (inputEnabled && InputBar != null)
|
|
{
|
|
ImGui.Separator();
|
|
InputBar.RenderCompact();
|
|
}
|
|
|
|
if (ImGui.IsWindowHovered(ImGuiHoveredFlags.ChildWindows))
|
|
LastActivityTime = FrameTime;
|
|
}
|
|
|
|
// Returns the vertical space consumed by the banner (0 when not shown).
|
|
private float DrawHintBannerIfNeeded()
|
|
{
|
|
if (Plugin.Config.SeenPopOutInputHint)
|
|
return 0f;
|
|
|
|
var hintText = Resources.HellionStrings.Popout_v060_HintText;
|
|
var ackLabel = Resources.HellionStrings.Popout_v060_HintAck;
|
|
var openLabel = Resources.HellionStrings.Popout_v060_HintOpenSettings;
|
|
|
|
var startY = ImGui.GetCursorPosY();
|
|
|
|
var bg = new System.Numerics.Vector4(0.16f, 0.20f, 0.28f, 1f);
|
|
ImGui.PushStyleColor(ImGuiCol.ChildBg, bg);
|
|
ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f);
|
|
|
|
var dismiss = false;
|
|
var openSettings = false;
|
|
using (
|
|
var child = ImRaii.Child(
|
|
"##v060-pop-out-hint",
|
|
new System.Numerics.Vector2(0f, 64f),
|
|
true
|
|
)
|
|
)
|
|
{
|
|
if (child)
|
|
{
|
|
ImGui.TextWrapped(hintText);
|
|
if (ImGui.Button(ackLabel))
|
|
dismiss = true;
|
|
ImGui.SameLine();
|
|
if (ImGui.Button(openLabel))
|
|
{
|
|
dismiss = true;
|
|
openSettings = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui.PopStyleVar();
|
|
ImGui.PopStyleColor();
|
|
ImGui.Spacing();
|
|
|
|
if (dismiss)
|
|
{
|
|
Plugin.Config.SeenPopOutInputHint = true;
|
|
ChatLogWindow.Plugin.SaveConfig();
|
|
Plugin.Log.Debug("Pop-Out input hint dismissed");
|
|
if (openSettings)
|
|
ChatLogWindow.Plugin.SettingsWindow.Toggle();
|
|
}
|
|
|
|
return ImGui.GetCursorPosY() - startY;
|
|
}
|
|
|
|
public override void PostDraw()
|
|
{
|
|
if (Idx >= 0 && Idx < ChatLogWindow.PopOutDocked.Count)
|
|
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
|
}
|
|
|
|
public override void OnClose()
|
|
{
|
|
ChatLogWindow.PopOutWindows.Remove(Tab.Identifier);
|
|
ChatLogWindow.Plugin.WindowSystem.RemoveWindow(this);
|
|
|
|
Tab.PopOut = false;
|
|
ChatLogWindow.Plugin.SaveConfig();
|
|
}
|
|
|
|
private enum HideState
|
|
{
|
|
None,
|
|
Cutscene,
|
|
CutsceneOverride,
|
|
User,
|
|
Battle,
|
|
}
|
|
|
|
private HideState CurrentHideState = HideState.None;
|
|
|
|
private bool HideStateCheck()
|
|
{
|
|
if (Tab.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
|
|
{
|
|
CurrentHideState = HideState.Battle;
|
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: None -> Battle");
|
|
}
|
|
|
|
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
|
|
{
|
|
CurrentHideState = HideState.None;
|
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: Battle -> None");
|
|
}
|
|
|
|
if (
|
|
Tab.HideDuringCutscenes
|
|
&& CurrentHideState == HideState.None
|
|
&& (Plugin.CutsceneActive || Plugin.GposeActive)
|
|
)
|
|
{
|
|
if (ChatLogWindow.Plugin.Functions.Chat.CheckHideFlags())
|
|
{
|
|
CurrentHideState = HideState.Cutscene;
|
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: None -> Cutscene");
|
|
}
|
|
}
|
|
|
|
if (
|
|
CurrentHideState is HideState.Cutscene or HideState.CutsceneOverride
|
|
&& !Plugin.CutsceneActive
|
|
&& !Plugin.GposeActive
|
|
)
|
|
{
|
|
Plugin.Log.Verbose(
|
|
$"Popout HideState [{Tab.Name}]: {CurrentHideState} -> None (cutscene/gpose ended)"
|
|
);
|
|
CurrentHideState = HideState.None;
|
|
}
|
|
|
|
if (CurrentHideState == HideState.Cutscene && ChatLogWindow.Activate)
|
|
{
|
|
CurrentHideState = HideState.CutsceneOverride;
|
|
Plugin.Log.Verbose(
|
|
$"Popout HideState [{Tab.Name}]: Cutscene -> CutsceneOverride (user activate)"
|
|
);
|
|
}
|
|
|
|
if (CurrentHideState == HideState.User && ChatLogWindow.Activate)
|
|
{
|
|
CurrentHideState = HideState.None;
|
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: User -> None (activate)");
|
|
}
|
|
|
|
return CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle
|
|
|| (Tab.HideWhenNotLoggedIn && !Plugin.ClientState.IsLoggedIn);
|
|
}
|
|
}
|