diff --git a/HellionChat/Ui/ChatLogWindow.cs b/HellionChat/Ui/ChatLogWindow.cs index a5446c3..36cec74 100644 --- a/HellionChat/Ui/ChatLogWindow.cs +++ b/HellionChat/Ui/ChatLogWindow.cs @@ -776,6 +776,10 @@ public sealed class ChatLogWindow : Window // (~17ms at 60fps) late, invisible inside the post-reload Atlas-Build. private bool _firstFrameDone; + // UI-5: set when the user clicks the scroll-to-bottom button; the next + // frame's scroll-snap check forces a jump to the live end. + private bool _scrollToBottomRequested; + public override void Draw() { DrewThisFrame = true; @@ -1558,10 +1562,13 @@ public sealed class ChatLogWindow : Window using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)) DrawMessages(tab, handler, false); - if (switchedTab || ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) + if (switchedTab || _scrollToBottomRequested || ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) ImGui.SetScrollHereY(1f); + _scrollToBottomRequested = false; handler.Draw(); + + DrawScrollToBottomButton(); } private void DrawLogTableStyle(Tab tab, PayloadHandler handler, bool switchedTab) @@ -1588,12 +1595,41 @@ public sealed class ChatLogWindow : Window // Custom styles can have cellPadding that go above 4, which GetScrollY isn't respecting var cellPaddingOffset = !compact && oldCellPadding.Y > 4f ? oldCellPadding.Y - 4f : 0f; - if (switchedTab || ImGui.GetScrollY() + cellPaddingOffset >= ImGui.GetScrollMaxY()) + if (switchedTab || _scrollToBottomRequested + || ImGui.GetScrollY() + cellPaddingOffset >= ImGui.GetScrollMaxY()) ImGui.SetScrollHereY(1f); + _scrollToBottomRequested = false; handler.Draw(); } } + + // 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() + { + if (ImGui.GetScrollMaxY() - ImGui.GetScrollY() <= 1f) + return; + + 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); + + if (ImGuiUtil.IconButton( + FontAwesomeIcon.ArrowDown, + tooltip: HellionStrings.ChatLog_ScrollToBottom_Tooltip)) + _scrollToBottomRequested = true; } private void DrawMessages(