fix(ui): draw scroll-to-bottom button in a standalone overlay window
Button drawn in the parent window over the ##chat2-messages child was never clickable: ImGui resolves g.HoveredWindow to the child for that screen rect, so ItemHoverable rejects any item submitted in the parent. A top-level Begin/End window is a sibling in the window list and wins the hit-test for its own rect. ownerId parameter keeps the window name distinct between the main window and each pop-out, preventing Begin/End collisions when both render in the same frame.
This commit is contained in:
@@ -1548,7 +1548,8 @@ public sealed class ChatLogWindow : Window
|
||||
Tab tab,
|
||||
PayloadHandler handler,
|
||||
float childHeight,
|
||||
bool switchedTab
|
||||
bool switchedTab,
|
||||
string ownerId = "main"
|
||||
)
|
||||
{
|
||||
// Capture these before entering the child so we can position the overlay
|
||||
@@ -1581,7 +1582,7 @@ public sealed class ChatLogWindow : Window
|
||||
// (which caused it to drift) and avoids the scrollbar's inner clip rect
|
||||
// (which caused it to be cut off on the right edge).
|
||||
if (_childScrolledUp)
|
||||
DrawScrollToBottomButtonOverlay(childScreenPos, childWidth, childHeight);
|
||||
DrawScrollToBottomButtonOverlay(childScreenPos, childWidth, childHeight, ownerId);
|
||||
}
|
||||
|
||||
private void DrawLogNormalStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
||||
@@ -1631,35 +1632,61 @@ public sealed class ChatLogWindow : Window
|
||||
|
||||
}
|
||||
|
||||
// UI-5: floating jump-to-latest button, drawn in the PARENT window after the
|
||||
// ##chat2-messages child closes. Placing it here prevents two bugs:
|
||||
// 1. Inside the child, SetCursorPos adds to CursorMaxPos.y and inflates
|
||||
// ContentSize.y / ScrollMaxY every frame, so the button chased its own
|
||||
// shadow and never landed at a stable position.
|
||||
// 2. GetWindowWidth() inside the child includes the scrollbar column, so the
|
||||
// button overlapped the scrollbar and was clipped by the inner clip rect.
|
||||
// Screen-space positioning via SetCursorScreenPos is immune to both.
|
||||
// UI-5: floating jump-to-latest button.
|
||||
//
|
||||
// Why a standalone overlay window rather than drawing in the parent?
|
||||
// When this button was drawn in the parent window after the ##chat2-messages
|
||||
// child closed, ImGui's hit-test still resolved g.HoveredWindow to the child
|
||||
// (the child occupies the same screen rect). ItemHoverable then rejected the
|
||||
// button submitted in the parent, so it was visible but never clickable.
|
||||
//
|
||||
// A top-level Begin/End window is a sibling in the window list, not nested
|
||||
// under the child, so it wins the hit-test for its own rect and the button
|
||||
// inside it is fully clickable.
|
||||
//
|
||||
// The ownerId parameter makes the window name unique per calling context so
|
||||
// the main window and each pop-out don't share a single ImGui window entry.
|
||||
private void DrawScrollToBottomButtonOverlay(
|
||||
Vector2 childScreenPos,
|
||||
float childWidth,
|
||||
float childHeight)
|
||||
float childHeight,
|
||||
string ownerId)
|
||||
{
|
||||
var size = ImGui.GetFrameHeight();
|
||||
var pad = 8f * ImGuiHelpers.GlobalScale;
|
||||
var scrollbarWidth = ImGui.GetStyle().ScrollbarSize;
|
||||
|
||||
// Position confirmed correct in-game: bottom-right of the chat child,
|
||||
// inset by pad, and pulled left of the vertical scrollbar.
|
||||
var btnPos = new Vector2(
|
||||
childScreenPos.X + childWidth - scrollbarWidth - size - pad,
|
||||
childScreenPos.Y + childHeight - size - pad);
|
||||
|
||||
ImGui.SetCursorScreenPos(btnPos);
|
||||
ImGui.SetNextWindowPos(btnPos, ImGuiCond.Always);
|
||||
ImGui.SetNextWindowSize(new Vector2(size, size), ImGuiCond.Always);
|
||||
|
||||
const ImGuiWindowFlags overlayFlags =
|
||||
ImGuiWindowFlags.NoTitleBar
|
||||
| ImGuiWindowFlags.NoResize
|
||||
| ImGuiWindowFlags.NoMove
|
||||
| ImGuiWindowFlags.NoScrollbar
|
||||
| ImGuiWindowFlags.NoScrollWithMouse
|
||||
| ImGuiWindowFlags.NoSavedSettings
|
||||
| ImGuiWindowFlags.NoFocusOnAppearing
|
||||
| ImGuiWindowFlags.NoBackground;
|
||||
|
||||
// End() must be called unconditionally after Begin() — ImGui rule.
|
||||
if (ImGui.Begin($"##scroll-to-bottom-overlay-{ownerId}", overlayFlags))
|
||||
{
|
||||
if (ImGuiUtil.IconButton(
|
||||
FontAwesomeIcon.ArrowDown,
|
||||
tooltip: HellionStrings.ChatLog_ScrollToBottom_Tooltip))
|
||||
_scrollToBottomRequested = true;
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
private void DrawMessages(
|
||||
Tab tab,
|
||||
PayloadHandler handler,
|
||||
|
||||
@@ -118,7 +118,7 @@ internal class Popout : Window
|
||||
|
||||
var handler = ChatLogWindow.HandlerLender.Borrow();
|
||||
var logHeight = ImGui.GetContentRegionAvail().Y - inputBarHeight - hintBannerHeight;
|
||||
ChatLogWindow.DrawMessageLog(Tab, handler, logHeight, false);
|
||||
ChatLogWindow.DrawMessageLog(Tab, handler, logHeight, false, Tab.Identifier.ToString());
|
||||
|
||||
if (inputEnabled && InputBar != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user