Pull the auto-scroll concern out of ConsoleTab into a focused, ImGui-free
ConsoleScrollController (console_scroll_controller.{h,cpp}). It owns the three
coupled fields that were loose on the god-class — auto_scroll_, the wheel-up
cooldown, and the new-lines-while-scrolled-up backlog count — and the state
machine tying them together: wheel-up detaches + starts a cooldown, the cooldown
gates the at-bottom re-enable check, backlog counting happens only while
detached, and re-pinning (checkbox / jump pill / reaching the bottom) clears it.
The view keeps only what needs ImGui: it measures scroll position, hands the
controller a plain `atBottom` bool + frame delta, and issues the actual
SetScrollHereY. ConsoleTab loses three more members and all the scattered
auto-scroll bookkeeping collapses to scroll_.* calls.
Adds testConsoleScrollController: pinned start ignores backlog, wheel-up detach +
cooldown gating, re-enable only when at bottom, checkbox toggle semantics, and
the jump-to-bottom pill. Full-node + lite build clean; ctest green; source
hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
2.4 KiB
C++
64 lines
2.4 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
//
|
|
// ConsoleScrollController — the console's auto-scroll state machine.
|
|
//
|
|
// It owns three coupled pieces of state that were previously loose fields on the god-class:
|
|
// - auto_scroll_ : whether output is pinned to the bottom (follows new lines)
|
|
// - a cooldown : a short grace period after a wheel-up so smooth-scroll can animate away
|
|
// from the bottom before the "am I at the bottom?" re-enable check runs
|
|
// - new_lines_ : how many lines arrived while scrolled up (drives the jump-to-bottom pill)
|
|
//
|
|
// It is ImGui-free: the view measures scroll positions and hands in a plain `atBottom` bool
|
|
// and a frame delta; the controller decides when to re-pin. That keeps the interplay
|
|
// (wheel-up pauses, cooldown gates the re-enable, new-line counting only while scrolled up)
|
|
// unit testable.
|
|
|
|
#pragma once
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class ConsoleScrollController {
|
|
public:
|
|
// Seconds to ignore the at-bottom re-enable check after a user wheel-up, so an in-flight
|
|
// smooth-scroll can move away from the bottom first.
|
|
static constexpr float kScrollUpCooldownSeconds = 0.5f;
|
|
|
|
bool autoScroll() const { return auto_scroll_; }
|
|
int newLines() const { return new_lines_; }
|
|
|
|
// User toggled the "auto-scroll" checkbox. Enabling re-pins to the bottom.
|
|
void setAutoScroll(bool on);
|
|
|
|
// User scrolled up with the wheel — stop following output and start the cooldown.
|
|
void onUserScrolledUp();
|
|
|
|
// Advance the cooldown timer by one frame.
|
|
void tickCooldown(float dt);
|
|
|
|
// The view reports whether the viewport is currently at the bottom. If auto-scroll is off
|
|
// and the cooldown has elapsed, reaching the bottom re-enables auto-scroll.
|
|
void considerReenable(bool atBottom);
|
|
|
|
// `n` new lines were appended to the model this frame; count them only while scrolled up.
|
|
void onLinesAdded(int n);
|
|
|
|
// Called each frame while pinned to the bottom (the view issues the actual scroll).
|
|
void resetNewLines() { new_lines_ = 0; }
|
|
|
|
// User clicked the jump-to-bottom pill.
|
|
void jumpToBottom() { pinToBottom(); }
|
|
|
|
private:
|
|
void pinToBottom() { auto_scroll_ = true; new_lines_ = 0; }
|
|
|
|
bool auto_scroll_ = true;
|
|
float cooldown_ = 0.0f;
|
|
int new_lines_ = 0;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|