`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>
145 lines
6.3 KiB
C++
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);
|
|
}
|