fix(ui): move scroll-to-bottom button into the chat header toolbar
Drop the three-attempt floating overlay entirely. The button now lives in the chat header toolbar (DrawScrollToBottomToolbarButton), visible only when the user is scrolled above the live end. Toolbar layout: honorific slot, scroll button, pop-out button flush-right -- pop-out position unchanged.
This commit is contained in:
@@ -1544,20 +1544,8 @@ public sealed class ChatLogWindow : Window
|
||||
CurrentHideState = HideState.User;
|
||||
}
|
||||
|
||||
internal void DrawMessageLog(
|
||||
Tab tab,
|
||||
PayloadHandler handler,
|
||||
float childHeight,
|
||||
bool switchedTab,
|
||||
string ownerId = "main"
|
||||
)
|
||||
internal void DrawMessageLog(Tab tab, PayloadHandler handler, float childHeight, bool switchedTab)
|
||||
{
|
||||
// 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;
|
||||
|
||||
using (var child = ImRaii.Child("##chat2-messages", new Vector2(-1, childHeight)))
|
||||
{
|
||||
if (child.Success)
|
||||
@@ -1567,8 +1555,9 @@ public sealed class ChatLogWindow : Window
|
||||
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.
|
||||
// Cached for the header toolbar's scroll-to-bottom button, which is
|
||||
// drawn one frame later. GetScrollMaxY / GetScrollY here refer to
|
||||
// the child's scroll context.
|
||||
_childScrolledUp = ImGui.GetScrollMaxY() - ImGui.GetScrollY() > 1f;
|
||||
}
|
||||
else
|
||||
@@ -1576,13 +1565,6 @@ public sealed class ChatLogWindow : Window
|
||||
_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, ownerId);
|
||||
}
|
||||
|
||||
private void DrawLogNormalStyle(Tab tab, PayloadHandler handler, bool switchedTab)
|
||||
@@ -1632,61 +1614,6 @@ public sealed class ChatLogWindow : Window
|
||||
|
||||
}
|
||||
|
||||
// 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,
|
||||
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.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,
|
||||
@@ -2390,14 +2317,42 @@ public sealed class ChatLogWindow : Window
|
||||
Plugin.WantedTab = null;
|
||||
}
|
||||
|
||||
// DrawChatHeaderToolbar: renders the pop-out button for the active tab.
|
||||
// v1.3.0 also renders the optional Honorific title slot left of it.
|
||||
// DrawChatHeaderToolbar: renders the honorific title slot, the optional
|
||||
// scroll-to-bottom button, and the pop-out button for the active tab.
|
||||
// v1.3.0 added the title slot; v1.5.5 added the scroll-to-bottom button.
|
||||
private void DrawChatHeaderToolbar(Tab tab)
|
||||
{
|
||||
DrawHonorificTitleSlot();
|
||||
DrawScrollToBottomToolbarButton();
|
||||
DrawPopOutButton(tab);
|
||||
}
|
||||
|
||||
// Draws an arrow-down button in the toolbar when the user has scrolled up
|
||||
// from the live end of the chat log. Clicking it requests a snap to bottom.
|
||||
//
|
||||
// _childScrolledUp is set at the end of DrawMessageLog, which runs AFTER
|
||||
// DrawChatHeaderToolbar in the same frame. So this button always reflects the
|
||||
// previous frame's scroll state, a one-frame lag that is imperceptible in use.
|
||||
//
|
||||
// Both this button and DrawPopOutButton use SetCursorPosX with absolute
|
||||
// positioning (cursorX + GetContentRegionAvail().X - N * iconWidth). Because
|
||||
// each call computes its own target X from the right edge, they are independent
|
||||
// of each other and of what the cursor position happens to be at call time.
|
||||
// The pop-out button lands at rightEdge - iconWidth regardless of call order.
|
||||
private void DrawScrollToBottomToolbarButton()
|
||||
{
|
||||
if (!_childScrolledUp)
|
||||
return;
|
||||
|
||||
var avail = ImGui.GetContentRegionAvail().X;
|
||||
var iconWidth = ImGui.GetFrameHeight();
|
||||
var spacing = ImGui.GetStyle().ItemSpacing.X;
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + avail - 2 * iconWidth - spacing);
|
||||
|
||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: HellionStrings.ChatLog_ScrollToBottom_Tooltip))
|
||||
_scrollToBottomRequested = true;
|
||||
}
|
||||
|
||||
private void DrawPopOutButton(Tab tab)
|
||||
{
|
||||
var avail = ImGui.GetContentRegionAvail().X;
|
||||
|
||||
@@ -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, Tab.Identifier.ToString());
|
||||
ChatLogWindow.DrawMessageLog(Tab, handler, logHeight, false);
|
||||
|
||||
if (inputEnabled && InputBar != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user