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.
|
// frame's scroll-snap check forces a jump to the live end.
|
||||||
private bool _scrollToBottomRequested;
|
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()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
DrewThisFrame = true;
|
DrewThisFrame = true;
|
||||||
@@ -1547,14 +1551,37 @@ public sealed class ChatLogWindow : Window
|
|||||||
bool switchedTab
|
bool switchedTab
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using var child = ImRaii.Child("##chat2-messages", new Vector2(-1, childHeight));
|
// Capture these before entering the child so we can position the overlay
|
||||||
if (!child.Success)
|
// button in the parent window's coordinate space afterward. Inside the
|
||||||
return;
|
// child the same queries would return child-local values.
|
||||||
|
var childScreenPos = ImGui.GetCursorScreenPos();
|
||||||
|
var childWidth = ImGui.GetContentRegionAvail().X;
|
||||||
|
|
||||||
if (tab.DisplayTimestamp && Plugin.Config.PrettierTimestamps)
|
using (var child = ImRaii.Child("##chat2-messages", new Vector2(-1, childHeight)))
|
||||||
DrawLogTableStyle(tab, handler, switchedTab);
|
{
|
||||||
else
|
if (child.Success)
|
||||||
DrawLogNormalStyle(tab, handler, switchedTab);
|
{
|
||||||
|
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)
|
private void DrawLogNormalStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
||||||
@@ -1567,8 +1594,6 @@ public sealed class ChatLogWindow : Window
|
|||||||
_scrollToBottomRequested = false;
|
_scrollToBottomRequested = false;
|
||||||
|
|
||||||
handler.Draw();
|
handler.Draw();
|
||||||
|
|
||||||
DrawScrollToBottomButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawLogTableStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
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
|
// UI-5: floating jump-to-latest button, drawn in the PARENT window after the
|
||||||
// scrolled up from the live end. Pinned to the bottom-right of the visible
|
// ##chat2-messages child closes. Placing it here prevents two bugs:
|
||||||
// viewport inside the scrolling child (cursor coords are content-space, the
|
// 1. Inside the child, SetCursorPos adds to CursorMaxPos.y and inflates
|
||||||
// viewport is [ScrollY, ScrollY + WindowHeight]).
|
// ContentSize.y / ScrollMaxY every frame, so the button chased its own
|
||||||
private void DrawScrollToBottomButton()
|
// 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)
|
var size = ImGui.GetFrameHeight();
|
||||||
return;
|
var pad = 8f * ImGuiHelpers.GlobalScale;
|
||||||
|
var scrollbarWidth = ImGui.GetStyle().ScrollbarSize;
|
||||||
|
|
||||||
var size = ImGui.GetFrameHeight();
|
var btnPos = new Vector2(
|
||||||
var pad = 8f * ImGuiHelpers.GlobalScale;
|
childScreenPos.X + childWidth - scrollbarWidth - size - pad,
|
||||||
ImGui.SetCursorPosX(ImGui.GetScrollX() + ImGui.GetWindowWidth() - size - pad);
|
childScreenPos.Y + childHeight - size - pad);
|
||||||
ImGui.SetCursorPosY(ImGui.GetScrollY() + ImGui.GetWindowHeight() - size - pad);
|
|
||||||
|
ImGui.SetCursorScreenPos(btnPos);
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(
|
if (ImGuiUtil.IconButton(
|
||||||
FontAwesomeIcon.ArrowDown,
|
FontAwesomeIcon.ArrowDown,
|
||||||
|
|||||||
Reference in New Issue
Block a user