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>
66 lines
2.6 KiB
C++
66 lines
2.6 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
//
|
|
// ConsoleSelectionController — owns the console's text-selection state (anchor + caret) and
|
|
// all the pure logic around it: the mouse-drag lifecycle, select-all, ordering, extraction,
|
|
// and the index shift applied when lines are evicted from the top by the buffer cap.
|
|
//
|
|
// It is deliberately ImGui-free. Screen->text hit-testing stays in the view (it needs the
|
|
// current frame's wrapped layout + output origin); the view feeds the resulting
|
|
// ConsoleTextPos values in here. That keeps the fiddly byte-offset / eviction math unit
|
|
// testable in isolation from the renderer.
|
|
|
|
#pragma once
|
|
|
|
#include "console_text_layout.h" // ConsoleTextPos, ConsoleLineAccessor, ExtractConsoleSelection
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class ConsoleSelectionController {
|
|
public:
|
|
// Mouse-drag lifecycle. Positions are raw {line, col}: line = index into the full model,
|
|
// col = byte offset within that line.
|
|
void beginDrag(ConsoleTextPos pos); // button press
|
|
void updateDrag(ConsoleTextPos pos); // movement while held
|
|
void endDrag(ConsoleTextPos pos); // release
|
|
|
|
// Select the whole buffer, [0,0] .. [lineCount-1, lastLineByteLen]. No-op if empty.
|
|
void selectAll(int lineCount, int lastLineByteLen);
|
|
|
|
// Drop the selection entirely (also ends any in-progress drag).
|
|
void clear();
|
|
|
|
bool active() const { return active_; } // a selection currently exists
|
|
bool dragging() const { return dragging_; } // a drag is in progress
|
|
|
|
// Ordered bounds, start <= end. Only meaningful while active().
|
|
ConsoleTextPos rangeStart() const;
|
|
ConsoleTextPos rangeEnd() const;
|
|
|
|
// After the cap evicts `popped` lines from the front, shift the selection up to track
|
|
// the same text. Drops the selection if it fell entirely off the top.
|
|
void shiftForEviction(int popped);
|
|
|
|
// Selected text, or "" when inactive. `visibleIndices` are the raw line indices currently
|
|
// shown (the selection spans raw lines but copies only the visible ones).
|
|
std::string extract(int lineCount, const ConsoleLineAccessor& getLine,
|
|
const std::vector<int>& visibleIndices) const;
|
|
|
|
// Position ordering: is a at or before b in reading order?
|
|
static bool isBeforeOrEqual(ConsoleTextPos a, ConsoleTextPos b);
|
|
|
|
private:
|
|
ConsoleTextPos anchor_{-1, 0}; // where the drag began / the selection origin
|
|
ConsoleTextPos caret_{-1, 0}; // current drag position / the selection focus
|
|
bool active_ = false;
|
|
bool dragging_ = false;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|