Files
essim/src/tui/tui.cpp
François 792e4745d3 Merge search and net screens into explore; drop both commands.
`explore` was already a superset of `search` (4 columns: module → type
→ filtered children → detail, with parts/signals/connections — vs
search's 2 columns of parts/signals only). It now also subsumes the
former `net` screen: when a signal entry is selected, the detail pane
shows the local pins followed by a `Net members (across connections)`
section listing every `(module, signal, type)` reachable through the
BFS over `Connection::pin_map`, with the count + dominant type and an
INCONSISTENT flag in the signal-detail header.

Removed:
- `src/tui/screen_search.cpp`, `src/tui/screen_net.cpp`.
- `commands["search"]`, `commands["net"]` (including its textual
  inline form). The `find_net` / `Net` API stays for explore's BFS
  panel and the analyze screen's net-mix check.
- `[s]` and `[n]` letter shortcuts on the dashboard.
- `net_*` and `search_*` state members + builders + constructor
  inits.

screen_idx renumbering (the slots vacated by search + net are
removed, not left dead):
  0 = console  (unchanged)
  1 = connect
  2 = set-connector-type
  3 = explore  (unchanged number, but now subsumes search + net)
  4 = dashboard (boot)
  5 = analyze

Palette signal items now jump to `explore` prefilled on the signals
tab with the child filter seeded to the exact signal name; the BFS
section in the detail pane is what shows the cross-module net.

Net-member rows in the detail pane are deliberately read-only for
now (Enter is a no-op): the signal-type popup is scoped to the
currently selected module, so opening it on a peer-module member
would mis-fire. Cross-module Enter navigation can come later if
needed.

DESIGN.md and user docs updated accordingly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 21:49:26 +02:00

145 lines
6.3 KiB
C++

#include "tui/tui.hpp"
#include "system/system.hpp"
#include <ftxui/component/component.hpp>
#include <ftxui/component/event.hpp>
#include <ftxui/component/screen_interactive.hpp>
using namespace ftxui;
Tui::Tui()
: cursor_pos(0), history_idx(-1), scroll_offset(0), quit(false), in_source(false),
loading(false), tick_in_flight(false),
loading_idx(0), loading_executed(0), loading_lineno(0),
loading_prev_in_source(false), screen_ptr(nullptr),
screen_idx(4), // boot to the dashboard; console (screen 0) is now a sub-screen
connect_m1_idx(0), connect_m2_idx(0),
connect_p1_idx(0), connect_p2_idx(0),
connect_focus_idx(0),
explore_module_idx(0),
explore_types{"parts", "signals", "connections"},
explore_type_idx(0), explore_child_idx(0),
explore_detail_idx(0), explore_focus_idx(0),
settype_m_idx(0), settype_p_idx(0), settype_focus_idx(0)
{
LoadHistory();
RegisterCommands();
Print("essim — type 'help' for commands, 'quit' to exit.");
}
Tui::~Tui() = default;
void Tui::Run() {
auto screen = ScreenInteractive::Fullscreen();
screen_ptr = &screen;
auto main_screen = BuildMainScreen(screen);
auto connect_screen = BuildConnectScreen();
auto settype_screen = BuildSettypeScreen();
auto explore_screen = BuildExploreScreen() | Modal(BuildSignalTypeModal(),
&sigtype_dialog_open);
auto dashboard_screen = BuildDashboardScreen();
auto analyze_screen = BuildAnalyzeScreen();
auto tab = Container::Tab(
{main_screen, connect_screen, settype_screen, explore_screen,
dashboard_screen, analyze_screen},
&screen_idx);
// Palette is a global Modal — overlays the tab on every screen.
auto with_palette = tab | Modal(BuildPaletteModal(), &palette_open);
auto root = CatchEvent(with_palette, [this](Event e) {
// Modals (palette + sigtype popup) own their events while open.
if (palette_open || sigtype_dialog_open) return false;
// Ctrl-P opens the palette from any screen.
if (e == Event::CtrlP) { OpenPalette(); return true; }
// screen_idx mapping: 0 = console, 1 = connect, 2 = set-connector-type,
// 3 = explore, 4 = dashboard (home), 5 = analyze.
switch (screen_idx) {
case 5: // analyze
if (e == Event::Escape) { screen_idx = 4; return true; }
if (e == Event::Tab || e == Event::ArrowRight) {
analyze_focus_idx = (analyze_focus_idx + 1) % 3;
return true;
}
if (e == Event::TabReverse || e == Event::ArrowLeft) {
analyze_focus_idx = (analyze_focus_idx + 2) % 3;
return true;
}
return false;
case 4: // dashboard (home)
if (e == Event::Escape) { return true; }
if (e == Event::PageDown) { dashboard_scroll_offset += 10; return true; }
if (e == Event::PageUp) {
dashboard_scroll_offset = std::max(0, dashboard_scroll_offset - 10);
return true;
}
if (e == Event::Home) { dashboard_scroll_offset = 0; return true; }
if (e == Event::End) { dashboard_scroll_offset = 100000; return true; }
if (e == Event::Character("q")) { Dispatch("quit"); return true; }
if (e == Event::Character("c")) { screen_idx = 0; return true; }
if (e == Event::Character("p")) { Dispatch("connect"); return true; }
if (e == Event::Character("t")) { Dispatch("set-connector-type"); return true; }
if (e == Event::Character("e")) { Dispatch("explore"); return true; }
if (e == Event::Character("a")) { screen_idx = 5; return true; }
return false;
case 3: // explore
if (e == Event::Escape) { screen_idx = 4; return true; }
if (e == Event::Tab) { explore_focus_idx = (explore_focus_idx + 1) % 6; return true; }
if (e == Event::TabReverse) { explore_focus_idx = (explore_focus_idx + 5) % 6; return true; }
return false;
case 2: // set-connector-type
if (e == Event::Escape) { screen_idx = 4; return true; }
if (e == Event::Tab) { settype_focus_idx = (settype_focus_idx + 1) % 5; return true; }
if (e == Event::TabReverse) { settype_focus_idx = (settype_focus_idx + 4) % 5; return true; }
return false;
case 1: // connect
if (e == Event::Escape) { screen_idx = 4; return true; }
if (e == Event::Tab) { connect_focus_idx = (connect_focus_idx + 1) % 7; return true; }
if (e == Event::TabReverse) { connect_focus_idx = (connect_focus_idx + 6) % 7; return true; }
return false;
default: // 0: main (console / log view)
if (e == Event::Special("\x02tick")) { ProcessNextSourceLine(); return true; }
if (e == Event::Escape) {
if (!pending.empty()) { CancelPending(); return true; }
screen_idx = 4; return true;
}
if (e == Event::PageUp) { scroll_offset += 10; return true; }
if (e == Event::PageDown) { scroll_offset = std::max(0, scroll_offset - 10); return true; }
if (e == Event::Home) { scroll_offset = (int)output.size(); return true; }
if (e == Event::End) { scroll_offset = 0; return true; }
if (e == Event::ArrowUp || e == Event::ArrowDown) {
if (pending.empty()) {
if (e == Event::ArrowUp) HistoryUp();
else HistoryDown();
}
return true;
}
if (e == Event::Tab) {
if (pending.empty()) {
CompleteInline();
} else {
switch (pending.front().completion) {
case Completion::Path: CompletePath(); break;
case Completion::Command: CompleteCommand(); break;
case Completion::None: break;
}
}
return true;
}
return false;
}
});
screen.Loop(root);
}