From 280526304d16c1c96a21417c612f549563b0734c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Wed, 13 May 2026 07:56:46 +0200 Subject: [PATCH] Signal-type popup, NC pin tagging, interactive viewer hygiene. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enter on a signal entry (net / explore) opens a modal popup to pick power / gnd / other. Recording is deduped: a sequence of toggles on the same signal collapses to a single `set-signal-type` line; no-op selections record nothing. - Bare interactive commands (the ones that open a full-screen mode) are no longer recorded by `script-save`. Their inline forms still are. Mutating actions inside a screen record their own canonical line. - Mentor importer treats signals whose name starts with `unconnected` as no-connect — the pin is kept on the part without a signal and tagged `ImportedUnconnected`. - `drop_singleton_signals` runs at the end of `load`: any signal with exactly one pin is detached (singletons are NC by definition); the pin is tagged `DroppedSingleton`. Count is reported inline. - `verify` gains a one-line orphan summary (imported NC / dropped singleton totals). Pins materialised by `FillIdentityNCs` are excluded via a `pin_map` filter — they are bridged to a real signal on the peer module and are not real NCs at system level. - NcOrigin tag is serialized in save snapshots as an optional 4th field on N records (backward-compatible). Co-Authored-By: Claude Opus 4.7 --- src/imports/import_mentor.cpp | 26 +++++++++++--- src/system/persist.cpp | 8 ++++- src/system/pins.cpp | 15 ++++++++ src/system/pins.hpp | 14 ++++++++ src/system/signal_type.hpp | 1 + src/system/signals.cpp | 28 ++++++++++++++- src/system/signals.hpp | 5 +++ src/system/syselmts.hpp | 10 ++++++ src/tui/commands.cpp | 31 +++++++++++++++- src/tui/screen_explore.cpp | 37 ++++++++++++++++--- src/tui/screen_net.cpp | 12 +++++-- src/tui/screen_sigtype_modal.cpp | 35 ++++++++++++++++++ src/tui/shell.cpp | 61 +++++++++++++++++++++++++++++++- src/tui/tui.cpp | 10 ++++-- src/tui/tui.hpp | 17 +++++++++ src/tui/tui_helpers.cpp | 1 + 16 files changed, 295 insertions(+), 16 deletions(-) create mode 100644 src/tui/screen_sigtype_modal.cpp diff --git a/src/imports/import_mentor.cpp b/src/imports/import_mentor.cpp index bacdd3d..90cf460 100644 --- a/src/imports/import_mentor.cpp +++ b/src/imports/import_mentor.cpp @@ -2,12 +2,26 @@ #include "system/pins.hpp" #include "system/parts.hpp" +#include #include #include #include using namespace std; +// Mentor netlists tag a no-connect pin with a signal name starting with +// 'unconnected' (e.g. 'unconnected', 'unconnected (by TERM)'). We keep the +// pin on the part but leave it unattached — it surfaces as `(NC)` in +// `explore` and is excluded from every net. +static bool is_nc_signal_name(const string &s) { + static const string nc = "unconnected"; + if (s.size() < nc.size()) return false; + for (size_t i = 0; i < nc.size(); ++i) + if (std::tolower(static_cast(s[i])) != nc[i]) + return false; + return true; +} + /** * @brief Enum representing the parsing state. * @@ -104,11 +118,15 @@ void ImportMentor::parse(Signals *signals) if (is_name_match) { auto pin = new Pin(names[0]); // Create a new pin with the first name. - Signal *s = nullptr; prt->add(pin); // Add the pin to the current part. - s = signals->merge(names[2]); // Merge the signal with module signals. - s->add(pin); // Add the pin to the signal pins list. - pin->connect(s); // Connect the pin to a signal. + if (names.size() > 2 && !is_nc_signal_name(names[2])) { + Signal *s = signals->merge(names[2]); + s->add(pin); + pin->connect(s); + } else { + // NC pin — kept on the part with no signal, tagged. + pin->nc_origin = NcOrigin::ImportedUnconnected; + } } } break; diff --git a/src/system/persist.cpp b/src/system/persist.cpp index 954c9d6..f6d4537 100644 --- a/src/system/persist.cpp +++ b/src/system/persist.cpp @@ -46,7 +46,10 @@ bool save_system(const System *sys, const std::string &filename, std::string &er for (auto &nkv : *p) { Pin *pin = nkv.second; Signal *s = pin->signal(); - f << "N\t" << pin->name << "\t" << (s ? s->name : "") << "\n"; + f << "N\t" << pin->name << "\t" << (s ? s->name : ""); + const char *otag = nc_origin_tag(pin->nc_origin); + if (*otag) f << "\t" << otag; + f << "\n"; } } // Signal types: only persist non-default (Other) overrides. @@ -127,6 +130,9 @@ System *restore_system(const std::string &filename, std::string &error) Signal *s = cur_mod->signals->merge(fs[2]); s->add(pin); pin->connect(s); + } else if (fs.size() >= 4) { + NcOrigin o; + if (nc_origin_from_tag(fs[3], o)) pin->nc_origin = o; } } else if (tag == "S") { if (!cur_mod) return fail("S outside module"); diff --git a/src/system/pins.cpp b/src/system/pins.cpp index 2133c34..0173e2a 100644 --- a/src/system/pins.cpp +++ b/src/system/pins.cpp @@ -6,6 +6,21 @@ Pin::Pin(std::string name) : SystemElement(name), sig(nullptr), prnt(nullptr), expected_signal_type(SignalType::Other) {}; +const char *nc_origin_tag(NcOrigin o) { + switch (o) { + case NcOrigin::ImportedUnconnected: return "U"; + case NcOrigin::DroppedSingleton: return "D"; + case NcOrigin::None: return ""; + } + return ""; +} + +bool nc_origin_from_tag(const std::string &tag, NcOrigin &out) { + if (tag == "U") { out = NcOrigin::ImportedUnconnected; return true; } + if (tag == "D") { out = NcOrigin::DroppedSingleton; return true; } + return false; +} + bool Pin::connected() { return sig != nullptr; diff --git a/src/system/pins.hpp b/src/system/pins.hpp index 159a5ca..c77d1a3 100644 --- a/src/system/pins.hpp +++ b/src/system/pins.hpp @@ -10,6 +10,19 @@ class Part; #pragma once class Signal; +// Why a pin has no Signal attached. Set at import/post-load; preserved by +// save/restore. Pins materialised by FillIdentityNCs keep `None` — they have +// no local signal but are bridged via a Connection::pin_map and verify is +// expected to filter them out via that bridge, not via this tag. +enum class NcOrigin { + None, ///< pin->sig != nullptr + ImportedUnconnected, ///< Mentor 'unconnected*' encountered at parse + DroppedSingleton, ///< drop_singleton_signals at end of load +}; + +const char *nc_origin_tag(NcOrigin o); ///< "", "U", "D" +bool nc_origin_from_tag(const std::string &tag, NcOrigin &out); + class Pin : public SystemElement { Signal *sig; @@ -17,6 +30,7 @@ public: Pin(std::string name); Part *prnt; ///< Pointer to the parent part. SignalType expected_signal_type; ///< Set from connector_type at set-type. + NcOrigin nc_origin = NcOrigin::None; bool connected(); Signal *signal() const { return sig; } void connect(Signal *signal); diff --git a/src/system/signal_type.hpp b/src/system/signal_type.hpp index 9a2dae2..c95efdd 100644 --- a/src/system/signal_type.hpp +++ b/src/system/signal_type.hpp @@ -8,5 +8,6 @@ enum class SignalType { Power, GndShield, Other }; const char *signal_type_name(SignalType t); bool signal_type_from_name(const std::string &s, SignalType &out); SignalType infer_signal_type(const std::string &signal_name); +SignalType next_signal_type(SignalType t); // Power → GndShield → Other → Power #endif // _SIGNAL_TYPE_HPP_ diff --git a/src/system/signals.cpp b/src/system/signals.cpp index 064ec1e..5279196 100644 --- a/src/system/signals.cpp +++ b/src/system/signals.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include const char *signal_type_name(SignalType t) { switch (t) { @@ -15,6 +15,15 @@ const char *signal_type_name(SignalType t) { return "other"; } +SignalType next_signal_type(SignalType t) { + switch (t) { + case SignalType::Power: return SignalType::GndShield; + case SignalType::GndShield: return SignalType::Other; + case SignalType::Other: return SignalType::Power; + } + return SignalType::Other; +} + bool signal_type_from_name(const std::string &s, SignalType &out) { std::string l = s; std::transform(l.begin(), l.end(), l.begin(), @@ -83,6 +92,23 @@ Signals::~Signals() { } } +int drop_singleton_signals(Signals *signals) { + if (!signals) return 0; + std::vector doomed; + for (auto &kv : *signals) + if (kv.second->size() == 1) doomed.push_back(kv.second); + for (Signal *s : doomed) { + // Detach the lone pin so it surfaces as `(NC)` in views. + for (auto &pkv : *s) { + pkv.second->connect(nullptr); + pkv.second->nc_origin = NcOrigin::DroppedSingleton; + } + signals->remove(s->name); + delete s; + } + return (int)doomed.size(); +} + void Signals::add(Signal *signal) { SystemElementContainer::add(signal); diff --git a/src/system/signals.hpp b/src/system/signals.hpp index 57eb579..86aede4 100644 --- a/src/system/signals.hpp +++ b/src/system/signals.hpp @@ -26,4 +26,9 @@ public: ~Signals(); }; +// Drop every signal whose pin set is of size 1 — by definition unconnected. +// The lone pin is detached (sig=nullptr) and the Signal object is removed +// from the container and deleted. Returns the number of signals dropped. +int drop_singleton_signals(Signals *signals); + #endif // _SIGNALS_HPP_ \ No newline at end of file diff --git a/src/system/syselmts.hpp b/src/system/syselmts.hpp index 8846eab..93463f3 100644 --- a/src/system/syselmts.hpp +++ b/src/system/syselmts.hpp @@ -161,6 +161,16 @@ public: * @param name Name of the element. * @return Pointer to the merged or newly created element. */ + /** + * @brief Removes the element with the given name from the container. + * The element itself is NOT deleted — caller owns it. + * @return True if an element was removed, false if the name was absent. + */ + bool remove(string name) + { + return content.erase(name) > 0; + } + T *merge(string name) { if (exists(name)) diff --git a/src/tui/commands.cpp b/src/tui/commands.cpp index 77a12f6..523f4bc 100644 --- a/src/tui/commands.cpp +++ b/src/tui/commands.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include void Tui::RegisterCommands() { @@ -132,9 +133,12 @@ void Tui::RegisterCommands() { try { sys->Load(args[0], args[1], t); Module *mod = sys->modules()->get(args[0]); + int dropped = drop_singleton_signals(mod->signals); Print("loaded '" + args[0] + "' from " + args[1]); Print(" parts: " + std::to_string(mod->size())); - Print(" signals: " + std::to_string(mod->signals->size())); + Print(" signals: " + std::to_string(mod->signals->size()) + + (dropped ? " (dropped " + std::to_string(dropped) + + " singleton/NC signal(s))" : "")); } catch (const std::exception &e) { Print(std::string("load failed: ") + e.what()); } @@ -249,6 +253,30 @@ void Tui::RegisterCommands() { Print("verify: " + std::to_string(inconsistent) + " inconsistent net(s) over " + std::to_string(bridged) + " bridged net(s) (" + std::to_string(nets.size()) + " total)."); + + // Orphan pin report. A pin is "orphan" if it came out of import (or + // post-import drop) with no signal, and is still not bridged to a + // real signal via any Connection::pin_map. Use `nc-export` for the + // per-pin list. + std::unordered_set bridged_pins; + for (auto &ckv : *sys->connections()) + for (auto &wp : ckv.second->pin_map) { + if (wp.first) bridged_pins.insert(wp.first); + if (wp.second) bridged_pins.insert(wp.second); + } + int orph_imported = 0, orph_dropped = 0; + for (auto &mkv : *sys->modules()) + for (auto &pkv : *mkv.second) + for (auto &nkv : *pkv.second) { + Pin *pin = nkv.second; + if (pin->signal() || bridged_pins.count(pin)) continue; + if (pin->nc_origin == NcOrigin::ImportedUnconnected) ++orph_imported; + else if (pin->nc_origin == NcOrigin::DroppedSingleton) ++orph_dropped; + } + Print("verify: " + std::to_string(orph_imported + orph_dropped) + + " orphan pin(s) at import (" + + std::to_string(orph_imported) + " imported NC, " + + std::to_string(orph_dropped) + " dropped singleton)."); }, true, "check pin roles locally and signal-type consistency across bridged nets" }; @@ -653,6 +681,7 @@ void Tui::RegisterCommands() { Pin *sn = nkv.second; Pin *dn = new Pin(sn->name); dn->expected_signal_type = sn->expected_signal_type; + dn->nc_origin = sn->nc_origin; dp->add(dn); if (sn->signal()) { Signal *ds = dst->signals->get(sn->signal()->name); diff --git a/src/tui/screen_explore.cpp b/src/tui/screen_explore.cpp index c89cd6c..693e157 100644 --- a/src/tui/screen_explore.cpp +++ b/src/tui/screen_explore.cpp @@ -29,9 +29,30 @@ Component Tui::BuildExploreScreen() { auto module_menu = Menu(&explore_modules, &explore_module_idx); auto type_menu = Menu(&explore_types, &explore_type_idx); auto child_filter = Input(&explore_child_filter, "filter…", pf_opt); - auto children_menu = Menu(&explore_children, &explore_child_idx); + + MenuOption child_opt = MenuOption::Vertical(); + child_opt.entries = &explore_children; + child_opt.selected = &explore_child_idx; + child_opt.on_enter = [this]() { + if (explore_type_idx != 1 || explore_children.empty()) return; + OpenSignalTypeDialog(explore_modules[explore_module_idx], + explore_children[explore_child_idx]); + }; + auto children_menu = Menu(child_opt); + auto detail_filter = Input(&explore_detail_filter, "filter…", pf_opt); - auto detail_menu = Menu(&explore_detail, &explore_detail_idx); + + MenuOption detail_opt = MenuOption::Vertical(); + detail_opt.entries = &explore_detail; + detail_opt.selected = &explore_detail_idx; + detail_opt.on_enter = [this]() { + if (explore_detail_idx < 0 + || explore_detail_idx >= (int)explore_detail_sig.size()) return; + const std::string &sig = explore_detail_sig[explore_detail_idx]; + if (sig.empty()) return; + OpenSignalTypeDialog(explore_modules[explore_module_idx], sig); + }; + auto detail_menu = Menu(detail_opt); auto components = Container::Vertical( {module_menu, type_menu, child_filter, children_menu, detail_filter, detail_menu}, @@ -93,6 +114,7 @@ Component Tui::BuildExploreScreen() { // bound to it can scroll with arrow keys when focused). explore_header = "(no system)"; explore_detail.clear(); + explore_detail_sig.clear(); if (cur_mod && !explore_children.empty()) { const std::string &cname = explore_children[explore_child_idx]; try { @@ -116,8 +138,12 @@ Component Tui::BuildExploreScreen() { std::string line = " " + r.first + std::string(maxw - r.first.size() + 2, ' ') + r.second; - if (keep_detail(line)) + if (keep_detail(line)) { explore_detail.push_back(line); + // "(NC)" → no underlying Signal to retype. + explore_detail_sig.push_back( + r.second == "(NC)" ? std::string{} : r.second); + } } } else if (explore_type_idx == 1) { Signal *s = cur_mod->signals->get(cname); @@ -165,6 +191,9 @@ Component Tui::BuildExploreScreen() { } if (explore_detail.empty()) explore_detail.push_back("(empty)"); + // Pad the parallel sig vector so any index into explore_detail is safe. + while (explore_detail_sig.size() < explore_detail.size()) + explore_detail_sig.push_back({}); if (explore_detail_idx < 0 || explore_detail_idx >= (int)explore_detail.size()) { explore_detail_idx = 0; @@ -207,7 +236,7 @@ Component Tui::BuildExploreScreen() { title, separator(), hbox({col1, separator(), col2, separator(), col3, separator(), col4}) | flex, - text(" Tab: cycle focus (incl. detail to scroll) | Esc: leave explore ") | dim, + text(" Tab: cycle focus | Enter (on a signal): set signal type | Esc: leave ") | dim, }) | border; } catch (const std::exception &e) { return vbox({ diff --git a/src/tui/screen_net.cpp b/src/tui/screen_net.cpp index 4000164..0e8d9e6 100644 --- a/src/tui/screen_net.cpp +++ b/src/tui/screen_net.cpp @@ -25,7 +25,15 @@ Component Tui::BuildNetScreen() { }; auto filter_input = Input(&net_sig_filter, "filter signals…", filter_opt); auto module_menu = Menu(&net_modules, &net_module_idx); - auto signal_menu = Menu(&net_sigs, &net_sig_idx); + + MenuOption sig_opt = MenuOption::Vertical(); + sig_opt.entries = &net_sigs; + sig_opt.selected = &net_sig_idx; + sig_opt.on_enter = [this]() { + if (net_modules.empty() || net_sigs.empty()) return; + OpenSignalTypeDialog(net_modules[net_module_idx], net_sigs[net_sig_idx]); + }; + auto signal_menu = Menu(sig_opt); auto components = Container::Vertical( {filter_input, module_menu, signal_menu}, &net_focus_idx); @@ -113,7 +121,7 @@ Component Tui::BuildNetScreen() { title, separator(), hbox({left, separator(), middle, separator(), right}) | flex, - text(" Tab: cycle focus (filter ↔ module ↔ signal) | Esc: leave net ") | dim, + text(" Tab: cycle focus | Enter (on signal): set signal type | Esc: leave ") | dim, }) | border; }); } diff --git a/src/tui/screen_sigtype_modal.cpp b/src/tui/screen_sigtype_modal.cpp new file mode 100644 index 0000000..7118b65 --- /dev/null +++ b/src/tui/screen_sigtype_modal.cpp @@ -0,0 +1,35 @@ +#include "tui/tui.hpp" + +#include +#include +#include +#include + +using namespace ftxui; + +Component Tui::BuildSignalTypeModal() { + sigtype_dialog_entries = {"power", "gnd", "other"}; + + MenuOption opt = MenuOption::Vertical(); + opt.entries = &sigtype_dialog_entries; + opt.selected = &sigtype_dialog_choice; + opt.on_enter = [this]() { ApplySignalTypeChoice(); }; + auto menu = Menu(opt); + + auto with_esc = CatchEvent(menu, [this](Event e) { + if (e == Event::Escape) { sigtype_dialog_open = false; return true; } + return false; + }); + + return Renderer(with_esc, [this, menu] { + return vbox({ + text(" signal type ") | bold, + separator(), + text(sigtype_dialog_mod + "/" + sigtype_dialog_sig) | dim, + separator(), + menu->Render(), + separator(), + text(" ↑/↓ select • Enter apply • Esc cancel ") | dim, + }) | border | size(WIDTH, GREATER_THAN, 32); + }); +} diff --git a/src/tui/shell.cpp b/src/tui/shell.cpp index c728c77..40203ee 100644 --- a/src/tui/shell.cpp +++ b/src/tui/shell.cpp @@ -1,9 +1,14 @@ #include "tui/tui.hpp" #include "tui/tui_helpers.hpp" +#include "system/modules.hpp" +#include "system/signals.hpp" +#include "system/system.hpp" + #include #include #include +#include #include #include #include @@ -11,6 +16,55 @@ #include #include +void Tui::OpenSignalTypeDialog(const std::string &mod_name, + const std::string &sig_name) { + if (!sys) return; + Signal *sig = nullptr; + try { + Module *m = sys->modules()->get(mod_name); + sig = m->signals->get(sig_name); + } catch (const std::exception &) { return; } + + sigtype_dialog_mod = mod_name; + sigtype_dialog_sig = sig_name; + switch (sig->type) { + case SignalType::Power: sigtype_dialog_choice = 0; break; + case SignalType::GndShield: sigtype_dialog_choice = 1; break; + default: sigtype_dialog_choice = 2; break; + } + sigtype_dialog_open = true; +} + +void Tui::ApplySignalTypeChoice() { + sigtype_dialog_open = false; + if (!sys) return; + SignalType t; + switch (sigtype_dialog_choice) { + case 0: t = SignalType::Power; break; + case 1: t = SignalType::GndShield; break; + default: t = SignalType::Other; break; + } + Signal *sig = nullptr; + try { + Module *m = sys->modules()->get(sigtype_dialog_mod); + sig = m->signals->get(sigtype_dialog_sig); + } catch (const std::exception &) { return; } + if (sig->type == t) return; // no-op, no record + sig->type = t; + if (in_source) return; + + // Dedup: if the immediately previous recorded line targets the same + // signal, replace it so a sequence of toggles collapses to one line. + std::string line = "set-signal-type " + sigtype_dialog_mod + " " + + sigtype_dialog_sig + " " + signal_type_name(t); + std::string prefix = "set-signal-type " + sigtype_dialog_mod + " " + + sigtype_dialog_sig + " "; + if (!recorded.empty() && recorded.back().rfind(prefix, 0) == 0) + recorded.back() = std::move(line); + else + recorded.push_back(std::move(line)); +} + void Tui::Print(const std::string &line) { output.push_back(line); scroll_offset = 0; // any new line snaps the view back to the tail @@ -136,7 +190,12 @@ void Tui::Finalize(const std::string &name, static const std::set no_record = { "clear", "help", "quit", "exit", "source", "script-save", }; - if (spec.scriptable && !no_record.count(name)) recorded.push_back(canonical); + // A bare invocation of an `interactive` command opens a full-screen mode + // rather than mutating state — skip it. Any mutating action taken inside + // that screen records its own canonical line via the action callbacks. + bool opens_screen = spec.interactive && args.empty(); + if (spec.scriptable && !opens_screen && !no_record.count(name)) + recorded.push_back(canonical); } std::string Tui::ExpandVars(const std::string &s) const { diff --git a/src/tui/tui.cpp b/src/tui/tui.cpp index 9255a28..38eea98 100644 --- a/src/tui/tui.cpp +++ b/src/tui/tui.cpp @@ -41,8 +41,10 @@ void Tui::Run() { auto search_screen = BuildSearchScreen(); auto connect_screen = BuildConnectScreen(); auto settype_screen = BuildSettypeScreen(); - auto explore_screen = BuildExploreScreen(); - auto net_screen = BuildNetScreen(); + auto explore_screen = BuildExploreScreen() | Modal(BuildSignalTypeModal(), + &sigtype_dialog_open); + auto net_screen = BuildNetScreen() | Modal(BuildSignalTypeModal(), + &sigtype_dialog_open); auto tab = Container::Tab( {main_screen, search_screen, connect_screen, settype_screen, explore_screen, @@ -50,6 +52,10 @@ void Tui::Run() { &screen_idx); auto root = CatchEvent(tab, [this](Event e) { + // The signal-type popup must own Escape / Tab while it's open so the + // outer switch doesn't yank us back to the main screen. + if (sigtype_dialog_open) return false; + switch (screen_idx) { case 5: // net if (e == Event::Escape) { screen_idx = 0; return true; } diff --git a/src/tui/tui.hpp b/src/tui/tui.hpp index cd29fee..e893cd8 100644 --- a/src/tui/tui.hpp +++ b/src/tui/tui.hpp @@ -86,6 +86,7 @@ class Tui { std::string explore_child_filter; std::string explore_detail_filter; std::vector explore_detail; + std::vector explore_detail_sig; ///< parallel: signal name per detail line (empty = no signal) int explore_detail_idx; std::string explore_header; int explore_focus_idx; @@ -109,6 +110,13 @@ class Tui { int net_sig_idx; int net_focus_idx; + // ---- Signal-type popup (shared between net + explore screens) ---- + bool sigtype_dialog_open = false; + std::string sigtype_dialog_mod; + std::string sigtype_dialog_sig; + std::vector sigtype_dialog_entries; ///< ["power","gnd","other"] + int sigtype_dialog_choice = 0; + // ---- Set-type screen state ---- std::vector settype_modules; int settype_m_idx; @@ -150,6 +158,14 @@ private: void CompletePath(size_t start = 0); void CompleteInline(); + // Open the signal-type popup for / (no-op if names don't resolve). + void OpenSignalTypeDialog(const std::string &mod_name, + const std::string &sig_name); + // Apply the selected type to the targeted signal, record a + // `set-signal-type` line (deduping consecutive edits of the same signal), + // and close the popup. + void ApplySignalTypeChoice(); + // Filtered part list rebuild (used by connect & set-type screens) void RefreshFilteredPartList(const std::vector &modules, int m_idx, @@ -164,6 +180,7 @@ private: ftxui::Component BuildSettypeScreen(); ftxui::Component BuildExploreScreen(); ftxui::Component BuildNetScreen(); + ftxui::Component BuildSignalTypeModal(); }; #endif // _TUI_HPP_ diff --git a/src/tui/tui_helpers.cpp b/src/tui/tui_helpers.cpp index 2de4ceb..dc2a7b5 100644 --- a/src/tui/tui_helpers.cpp +++ b/src/tui/tui_helpers.cpp @@ -3,6 +3,7 @@ #include #include + std::string ToLower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });