Pull the text-selection concern out of ConsoleTab into a focused, ImGui-free
ConsoleSelectionController (console_selection_controller.{h,cpp}). It owns the
anchor/caret state and all the fiddly logic around it: the mouse-drag lifecycle
(beginDrag/updateDrag/endDrag), select-all, range ordering, text extraction over
the visible model, and the index shift applied when the buffer cap evicts lines
from the top.
ConsoleTab now delegates: the four scattered selection fields (is_selecting_,
has_selection_, sel_anchor_, sel_end_) and five helper methods (selectionStart/
End, isPosBeforeOrEqual, getSelectedText, clearSelection) collapse to a single
selection_ member plus a thin selectedText() wrapper. screenToTextPos (still
view-side — it needs the frame's layout) now returns the shared ConsoleTextPos,
so ConsoleTab::TextPos is retired.
The byte-offset / eviction math was previously only reachable through the live
ImGui renderer; it now has direct unit coverage (testConsoleSelectionController:
ordering, drag lifecycle, upward drag, select-all, partial/full/no-op eviction
shift, and filtered extraction). Full-node + lite build clean; ctest green;
source hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
90 lines
2.4 KiB
C++
90 lines
2.4 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "console_selection_controller.h"
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
bool ConsoleSelectionController::isBeforeOrEqual(ConsoleTextPos a, ConsoleTextPos b)
|
|
{
|
|
if (a.line < b.line) return true;
|
|
if (a.line > b.line) return false;
|
|
return a.col <= b.col;
|
|
}
|
|
|
|
void ConsoleSelectionController::beginDrag(ConsoleTextPos pos)
|
|
{
|
|
anchor_ = pos;
|
|
caret_ = pos;
|
|
dragging_ = true;
|
|
active_ = false; // not a real selection until the caret moves off the anchor
|
|
}
|
|
|
|
void ConsoleSelectionController::updateDrag(ConsoleTextPos pos)
|
|
{
|
|
caret_ = pos;
|
|
// Becomes a real selection once the caret leaves the anchor; never downgrades back.
|
|
if (caret_.line != anchor_.line || caret_.col != anchor_.col)
|
|
active_ = true;
|
|
}
|
|
|
|
void ConsoleSelectionController::endDrag(ConsoleTextPos pos)
|
|
{
|
|
caret_ = pos;
|
|
dragging_ = false;
|
|
}
|
|
|
|
void ConsoleSelectionController::selectAll(int lineCount, int lastLineByteLen)
|
|
{
|
|
if (lineCount <= 0) return;
|
|
anchor_ = {0, 0};
|
|
caret_ = {lineCount - 1, lastLineByteLen};
|
|
active_ = true;
|
|
}
|
|
|
|
void ConsoleSelectionController::clear()
|
|
{
|
|
active_ = false;
|
|
dragging_ = false;
|
|
anchor_ = {-1, 0};
|
|
caret_ = {-1, 0};
|
|
}
|
|
|
|
ConsoleTextPos ConsoleSelectionController::rangeStart() const
|
|
{
|
|
return isBeforeOrEqual(anchor_, caret_) ? anchor_ : caret_;
|
|
}
|
|
|
|
ConsoleTextPos ConsoleSelectionController::rangeEnd() const
|
|
{
|
|
return isBeforeOrEqual(anchor_, caret_) ? caret_ : anchor_;
|
|
}
|
|
|
|
void ConsoleSelectionController::shiftForEviction(int popped)
|
|
{
|
|
if (popped <= 0 || !active_) return;
|
|
anchor_.line -= popped;
|
|
caret_.line -= popped;
|
|
if (anchor_.line < 0 && caret_.line < 0) {
|
|
// Entire selection was in the removed range.
|
|
active_ = false;
|
|
dragging_ = false;
|
|
} else {
|
|
if (anchor_.line < 0) { anchor_.line = 0; anchor_.col = 0; }
|
|
if (caret_.line < 0) { caret_.line = 0; caret_.col = 0; }
|
|
}
|
|
}
|
|
|
|
std::string ConsoleSelectionController::extract(int lineCount, const ConsoleLineAccessor& getLine,
|
|
const std::vector<int>& visibleIndices) const
|
|
{
|
|
if (!active_) return "";
|
|
return ExtractConsoleSelection(lineCount, getLine, visibleIndices,
|
|
rangeStart(), rangeEnd());
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|