#pragma once #include "config.h" #include "source.h" #include "trigger_engine.h" #include #include #include #include struct GLFWwindow; struct ImFont; /** * ImGui + ImPlot renderer. * * Features: * - Multiple sources, signal sidebar with colour/style controls * - 8 plot grid layouts (1×1 … 4×1, 1×2 … 1×4, 2×2) * - Resizable sidebar via drag-splitter * - Live scrolling, pause/zoom, dual A/B cursors with ΔT * - Trigger: any signal, edge, pre/post window, single/normal modes * - Per-source statistics panel * - Polished dark theme with Inter + JetBrains Mono fonts */ class Renderer { public: explicit Renderer(const AppConfig& cfg); ~Renderer(); bool init(); void run(); void add_source(const std::string& label, const std::string& host, uint16_t port, const std::string& multicast_group = "", uint16_t data_port = 0); private: // ── Frame ───────────────────────────────────────────────────────── void render_frame(); // ── Style / fonts ───────────────────────────────────────────────── void apply_dark_style(); void apply_implot_style(); void load_fonts(); // ── UI panels ───────────────────────────────────────────────────── void draw_toolbar(); void draw_sidebar(); void draw_layout_popup(); // called from toolbar void draw_plots(float w, float h); void draw_single_plot(int plot_id, float w, float h); void draw_trigger_panel(); void draw_stats_panel(); void draw_add_source_modal(); void draw_signal_style_popup(); // ── Helpers ─────────────────────────────────────────────────────── double compute_latest_time() const; void poll_trigger(); // ── Configuration ───────────────────────────────────────────────── AppConfig cfg_; // ── Window / fonts ──────────────────────────────────────────────── GLFWwindow* window_ = nullptr; ImFont* font_ui_ = nullptr; ImFont* font_mono_= nullptr; // ── Sources ─────────────────────────────────────────────────────── mutable std::mutex sources_mutex_; std::vector> sources_; // ── Global UI state ─────────────────────────────────────────────── bool paused_ = false; bool live_mode_ = true; double window_sec_= 5.0; double view_t_max_= 0.0; public: enum class Layout { L1x1, L1x2, L1x3, L1x4, L2x1, L2x2, L3x1, L4x1 }; private: Layout layout_ = Layout::L1x1; // Resizable sidebar float sidebar_w_ = 240.f; // Panel visibility toggles bool show_trigger_ = false; bool show_stats_ = false; // ── Cursor state ────────────────────────────────────────────────── bool cursor_a_active_ = false; bool cursor_b_active_ = false; double cursor_a_time_ = 0.0; double cursor_b_time_ = 0.0; // ── Trigger state ───────────────────────────────────────────────── TriggerEngine trig_engine_; int trig_src_idx_ = 0; int trig_sig_idx_ = 0; bool trig_show_capture_ = false; std::vector cap_t_, cap_v_; size_t trig_last_written_ = 0; // Trigger controls (UI state) int trig_edge_sel_ = 0; int trig_mode_sel_ = 0; float trig_threshold_ = 0.f; float trig_pre_sec_ = 0.01f; float trig_post_sec_ = 0.09f; // ── Popups ──────────────────────────────────────────────────────── bool open_add_source_modal_ = false; char new_label_[128] = {}; char new_host_ [256] = {}; int new_port_ = 44500; char new_mcast_group_[64] = {}; // "" = unicast int new_data_port_ = 0; // 0 = port+1 int style_src_idx_ = -1; int style_sig_idx_ = -1; };