fix(ui): render scroll-to-bottom button as a parent overlay
The button was drawn inside the ##chat2-messages child via SetCursorPos, which inflated ContentSize.y / ScrollMaxY each frame (causing positional drift) and was clipped by the scrollbar's inner clip rect (causing right- edge cutoff). Move it to the parent window using screen-space coordinates captured before the child opens; the scroll state is cached inside the child while GetScrollMaxY/Y still refer to the child's scroll context.
This commit is contained in:
@@ -780,6 +780,10 @@ public sealed class ChatLogWindow : Window
|
||||
// frame's scroll-snap check forces a jump to the live end.
|
||||
private bool _scrollToBottomRequested;
|
||||
|
||||
// UI-5: cached each frame inside the ##chat2-messages child. True when the
|
||||
// user has scrolled up enough that the overlay button should be shown.
|
||||
private bool _childScrolledUp;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
DrewThisFrame = true;
|
||||
@@ -1547,14 +1551,37 @@ public sealed class ChatLogWindow : Window
|
||||
bool switchedTab
|
||||
)
|
||||
{
|
||||
using var child = ImRaii.Child("##chat2-messages", new Vector2(-1, childHeight));
|
||||
if (!child.Success)
|
||||
return;
|
||||
// Capture these before entering the child so we can position the overlay
|
||||
// button in the parent window's coordinate space afterward. Inside the
|
||||
// child the same queries would return child-local values.
|
||||
var childScreenPos = ImGui.GetCursorScreenPos();
|
||||
var childWidth = ImGui.GetContentRegionAvail().X;
|
||||
|
||||
if (tab.DisplayTimestamp && Plugin.Config.PrettierTimestamps)
|
||||
DrawLogTableStyle(tab, handler, switchedTab);
|
||||
else
|
||||
DrawLogNormalStyle(tab, handler, switchedTab);
|
||||
using (var child = ImRaii.Child("##chat2-messages", new Vector2(-1, childHeight)))
|
||||
{
|
||||
if (child.Success)
|
||||
{
|
||||
if (tab.DisplayTimestamp && Plugin.Config.PrettierTimestamps)
|
||||
DrawLogTableStyle(tab, handler, switchedTab);
|
||||
else
|
||||
DrawLogNormalStyle(tab, handler, switchedTab);
|
||||
|
||||
// Cache scroll state while we are still inside the child so
|
||||
// GetScrollMaxY / GetScrollY refer to the child's scroll context.
|
||||
_childScrolledUp = ImGui.GetScrollMaxY() - ImGui.GetScrollY() > 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_childScrolledUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the overlay button in the parent window, outside the child.
|
||||
// This prevents the button from inflating ScrollMaxY inside the child
|
||||
// (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);
|
||||
}
|
||||
|
||||
private void DrawLogNormalStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
||||
@@ -1567,8 +1594,6 @@ public sealed class ChatLogWindow : Window
|
||||
_scrollToBottomRequested = false;
|
||||
|
||||
handler.Draw();
|
||||
|
||||
DrawScrollToBottomButton();
|
||||
}
|
||||
|
||||
private void DrawLogTableStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
||||
@@ -1604,27 +1629,30 @@ public sealed class ChatLogWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the scroll-to-bottom button after EndTable so it renders as a
|
||||
// post-table overlay, not inside an active table cell. The outer
|
||||
// ItemSpacing=Zero push has ended; restore normal spacing so the button
|
||||
// position matches the DrawLogNormalStyle path.
|
||||
using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, oldItemSpacing))
|
||||
DrawScrollToBottomButton();
|
||||
}
|
||||
|
||||
// UI-5: floating jump-to-latest button, shown only while the user has
|
||||
// scrolled up from the live end. Pinned to the bottom-right of the visible
|
||||
// viewport inside the scrolling child (cursor coords are content-space, the
|
||||
// viewport is [ScrollY, ScrollY + WindowHeight]).
|
||||
private void DrawScrollToBottomButton()
|
||||
// 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.
|
||||
private void DrawScrollToBottomButtonOverlay(
|
||||
Vector2 childScreenPos,
|
||||
float childWidth,
|
||||
float childHeight)
|
||||
{
|
||||
if (ImGui.GetScrollMaxY() - ImGui.GetScrollY() <= 1f)
|
||||
return;
|
||||
var size = ImGui.GetFrameHeight();
|
||||
var pad = 8f * ImGuiHelpers.GlobalScale;
|
||||
var scrollbarWidth = ImGui.GetStyle().ScrollbarSize;
|
||||
|
||||
var size = ImGui.GetFrameHeight();
|
||||
var pad = 8f * ImGuiHelpers.GlobalScale;
|
||||
ImGui.SetCursorPosX(ImGui.GetScrollX() + ImGui.GetWindowWidth() - size - pad);
|
||||
ImGui.SetCursorPosY(ImGui.GetScrollY() + ImGui.GetWindowHeight() - size - pad);
|
||||
var btnPos = new Vector2(
|
||||
childScreenPos.X + childWidth - scrollbarWidth - size - pad,
|
||||
childScreenPos.Y + childHeight - size - pad);
|
||||
|
||||
ImGui.SetCursorScreenPos(btnPos);
|
||||
|
||||
if (ImGuiUtil.IconButton(
|
||||
FontAwesomeIcon.ArrowDown,
|
||||
|
||||
Reference in New Issue
Block a user