Files
ObsidianDragon/src/ui/windows/console_tab.h
dan_s 96c27bb949 feat: Full UI internationalization, pool hashrate stats, and layout caching
- Replace all hardcoded English strings with TR() translation keys across
  every tab, dialog, and component (~20 UI files)
- Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with
  complete translations (~37k lines added)
- Improve i18n loader with exe-relative path fallback and English base
  fallback for missing keys
- Add pool-side hashrate polling via pool stats API in xmrig_manager
- Introduce Layout::beginFrame() per-frame caching and refresh balance
  layout config only on schema generation change
- Offload daemon output parsing to worker thread
- Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
2026-03-11 00:40:50 -05:00

162 lines
5.6 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include "../layout.h"
#include "../../daemon/embedded_daemon.h"
#include "../../daemon/xmrig_manager.h"
#include "../../rpc/rpc_client.h"
#include "../../rpc/rpc_worker.h"
#include <string>
#include <vector>
#include <deque>
#include <mutex>
namespace dragonx {
namespace ui {
/**
* @brief Console tab for daemon output and command input
*
* Shows dragonxd output and allows executing RPC commands.
*/
class ConsoleTab {
public:
ConsoleTab();
~ConsoleTab() = default;
/**
* @brief Render the console tab
* @param daemon Pointer to embedded daemon (may be null)
* @param rpc Pointer to RPC client for command execution
* @param xmrig Pointer to xmrig manager for pool mining output (may be null)
*/
void render(daemon::EmbeddedDaemon* daemon, rpc::RPCClient* rpc, rpc::RPCWorker* worker, daemon::XmrigManager* xmrig = nullptr);
/**
* @brief Render the RPC Command Reference popup at top-level scope.
* Must be called outside any child window so the modal blocks all input.
*/
void renderCommandsPopupModal();
/**
* @brief Add a line to the console output
*/
void addLine(const std::string& line, ImU32 color = IM_COL32(200, 200, 200, 255));
/**
* @brief Clear console output
*/
void clear();
/**
* @brief Check if auto-scroll is enabled
*/
bool isAutoScrollEnabled() const { return auto_scroll_; }
// Scanline effect toggle (set from settings)
static bool s_scanline_enabled;
// Console output zoom factor (1.0 = default caption font size)
static float s_console_zoom;
// Show/hide daemon output messages
static bool s_daemon_messages_enabled;
// Show only error messages (filter toggle)
static bool s_errors_only_enabled;
/// Refresh console text colors for current theme (call after theme switch)
static void refreshColors();
// Colors — follow active theme (public for use by notification forwarding)
static ImU32 COLOR_COMMAND;
static ImU32 COLOR_RESULT;
static ImU32 COLOR_ERROR;
static ImU32 COLOR_DAEMON;
static ImU32 COLOR_INFO;
private:
struct ConsoleLine {
std::string text;
ImU32 color;
};
void executeCommand(const std::string& cmd, rpc::RPCClient* rpc, rpc::RPCWorker* worker);
void addCommandResult(const std::string& cmd, const std::string& result, bool is_error = false);
void renderToolbar(daemon::EmbeddedDaemon* daemon);
void renderOutput();
void renderInput(rpc::RPCClient* rpc, rpc::RPCWorker* worker);
void renderCommandsPopup();
// Selection helpers
void handleSelection();
std::string getSelectedText() const;
void clearSelection();
// Convert screen position to line/column
struct TextPos {
int line = -1;
int col = 0;
};
TextPos screenToTextPos(ImVec2 screen_pos, float line_height) const;
bool isPosBeforeOrEqual(const TextPos& a, const TextPos& b) const;
TextPos selectionStart() const; // Returns the earlier of sel_anchor_ and sel_end_
TextPos selectionEnd() const; // Returns the later of sel_anchor_ and sel_end_
std::deque<ConsoleLine> lines_;
std::vector<std::string> command_history_;
int history_index_ = -1;
char input_buffer_[4096] = {0};
bool auto_scroll_ = true;
bool scroll_to_bottom_ = false;
float scroll_up_cooldown_ = 0.0f; // seconds to wait before re-enabling auto-scroll
int new_lines_since_scroll_ = 0; // new lines while scrolled up (for indicator)
size_t last_daemon_output_size_ = 0;
size_t last_xmrig_output_size_ = 0;
bool shown_startup_message_ = false;
daemon::EmbeddedDaemon::State last_daemon_state_ = daemon::EmbeddedDaemon::State::Stopped;
bool last_rpc_connected_ = false;
// Text selection state
bool is_selecting_ = false;
bool has_selection_ = false;
TextPos sel_anchor_; // Where the mouse was first pressed
TextPos sel_end_; // Where the mouse currently is / was released
float output_scroll_y_ = 0.0f; // Track scroll position for selection
ImVec2 output_origin_ = {0, 0}; // Top-left of output area
float output_line_height_ = 0.0f; // Text line height (for scanline alignment)
std::mutex lines_mutex_;
// Output filter
char filter_text_[128] = {0};
mutable std::vector<int> visible_indices_; // Cached for selection mapping
// Wrapped line height caching (for variable-height text wrapping)
mutable std::vector<float> wrapped_heights_; // Height of each visible line (accounts for wrapping)
mutable std::vector<float> cumulative_y_offsets_; // Cumulative Y offset for each visible line
mutable float total_wrapped_height_ = 0.0f; // Total height of all visible lines
mutable float cached_wrap_width_ = 0.0f; // Wrap width used for cached heights
// Sub-row layout: each visible line is split into wrap segments so
// selection and hit-testing know the exact screen position of every
// character.
struct WrapSegment {
int byteStart; // byte offset into ConsoleLine::text
int byteEnd; // byte offset past last char in this segment
float yOffset; // Y offset of this segment relative to the line's top
float height; // visual height of this segment
};
mutable std::vector<std::vector<WrapSegment>> visible_wrap_segments_; // [vi] -> segments
// Commands popup
bool show_commands_popup_ = false;
};
} // namespace ui
} // namespace dragonx