wx: drive the edit ops from the tree selection + a right-click menu
Each tree node now carries a NodeData (kind + Module/Part/Signal pointers), so
edits can act on what's selected instead of always re-asking:
- Set connector type / Attach BSDL / Connect act on the selected Part (or the
Pin's owning part); Connect uses it as the first endpoint, then prompts for
the second. Set signal type acts on the selected Signal.
- With nothing relevant selected, each falls back to its modal picker, so the
menu-driven flow still works.
- Right-click a Part or Signal → a context menu of the actions valid for it;
the items reuse the menu IDs and select the clicked node first, so they run
the same handlers.
wx-only; builds clean, window opens with no asserts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,39 @@ wxString type_suffix(SignalType t) {
|
||||
return t == SignalType::Other ? wxString()
|
||||
: " (" + wxString(signal_type_name(t)) + ")";
|
||||
}
|
||||
|
||||
// What a tree node stands for, attached to the item so a selection or a
|
||||
// right-click can drive the edit operations on the right domain object.
|
||||
struct NodeData : public wxTreeItemData {
|
||||
enum class Kind { Other, Module, Part, Pin, Signal };
|
||||
Kind kind;
|
||||
Module *module = nullptr;
|
||||
Part *part = nullptr;
|
||||
Signal *signal = nullptr;
|
||||
explicit NodeData(Kind k) : kind(k) {}
|
||||
};
|
||||
|
||||
NodeData *node_of(wxTreeCtrl *tree, const wxTreeItemId &id) {
|
||||
return id.IsOk() ? static_cast<NodeData *>(tree->GetItemData(id)) : nullptr;
|
||||
}
|
||||
|
||||
// The part of the current selection — a Part node, or the Pin's owning part.
|
||||
Part *selected_part(wxTreeCtrl *tree) {
|
||||
NodeData *d = node_of(tree, tree->GetSelection());
|
||||
if (d && (d->kind == NodeData::Kind::Part || d->kind == NodeData::Kind::Pin))
|
||||
return d->part;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The signal of the current selection (and, via `mod`, its module).
|
||||
Signal *selected_signal(wxTreeCtrl *tree, Module **mod) {
|
||||
NodeData *d = node_of(tree, tree->GetSelection());
|
||||
if (d && d->kind == NodeData::Kind::Signal) {
|
||||
if (mod) *mod = d->module;
|
||||
return d->signal;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
EssimFrame::EssimFrame(WxFrontend &fe)
|
||||
@@ -145,6 +178,8 @@ EssimFrame::EssimFrame(WxFrontend &fe)
|
||||
Bind(wxEVT_MENU, &EssimFrame::OnQuit, this, ID_QUIT);
|
||||
Bind(wxEVT_MENU, &EssimFrame::OnAbout, this, ID_ABOUT);
|
||||
|
||||
tree_->Bind(wxEVT_TREE_ITEM_MENU, &EssimFrame::OnTreeContextMenu, this);
|
||||
|
||||
RebuildModelView();
|
||||
}
|
||||
|
||||
@@ -173,6 +208,11 @@ void EssimFrame::RebuildModelView() {
|
||||
wxTreeItemId mid = tree_->AppendItem(
|
||||
root, wx(mname) + wxString::Format(" — %d part(s), %d signal(s)",
|
||||
mp, ms));
|
||||
{
|
||||
auto *d = new NodeData(NodeData::Kind::Module);
|
||||
d->module = m;
|
||||
tree_->SetItemData(mid, d);
|
||||
}
|
||||
|
||||
// Parts → pins (each pin shows the signal it is wired to, or NC).
|
||||
std::vector<std::string> parts;
|
||||
@@ -185,6 +225,12 @@ void EssimFrame::RebuildModelView() {
|
||||
if (!p->connector_type.empty())
|
||||
label += " [" + wx(p->connector_type) + "]";
|
||||
wxTreeItemId pid = tree_->AppendItem(mid, label);
|
||||
{
|
||||
auto *d = new NodeData(NodeData::Kind::Part);
|
||||
d->module = m;
|
||||
d->part = p;
|
||||
tree_->SetItemData(pid, d);
|
||||
}
|
||||
|
||||
std::vector<std::string> pins;
|
||||
for (auto &nkv : *p) pins.push_back(nkv.first);
|
||||
@@ -202,7 +248,11 @@ void EssimFrame::RebuildModelView() {
|
||||
pl += ", dropped";
|
||||
pl += ")";
|
||||
}
|
||||
tree_->AppendItem(pid, pl);
|
||||
wxTreeItemId nid = tree_->AppendItem(pid, pl);
|
||||
auto *d = new NodeData(NodeData::Kind::Pin);
|
||||
d->module = m;
|
||||
d->part = p;
|
||||
tree_->SetItemData(nid, d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,8 +265,13 @@ void EssimFrame::RebuildModelView() {
|
||||
std::sort(sigs.begin(), sigs.end(), natural_less);
|
||||
for (const auto &sname : sigs) {
|
||||
Signal *s = m->signals->get(sname);
|
||||
tree_->AppendItem(sid, wx(sname) + type_suffix(s->type)
|
||||
+ wxString::Format(" — %d pin(s)", (int)s->size()));
|
||||
wxTreeItemId nid = tree_->AppendItem(
|
||||
sid, wx(sname) + type_suffix(s->type)
|
||||
+ wxString::Format(" — %d pin(s)", (int)s->size()));
|
||||
auto *d = new NodeData(NodeData::Kind::Signal);
|
||||
d->module = m;
|
||||
d->signal = s;
|
||||
tree_->SetItemData(nid, d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +423,8 @@ Part *EssimFrame::PickPart(const wxString &caption) {
|
||||
|
||||
|
||||
void EssimFrame::OnSetConnectorType(wxCommandEvent &) {
|
||||
Part *p = PickPart();
|
||||
Part *p = selected_part(tree_);
|
||||
if (!p) p = PickPart();
|
||||
if (!p) return;
|
||||
|
||||
wxTextEntryDialog dlg(this, "Connector type (empty = none):",
|
||||
@@ -392,7 +448,8 @@ void EssimFrame::OnSetConnectorType(wxCommandEvent &) {
|
||||
}
|
||||
|
||||
void EssimFrame::OnAttachBsdl(wxCommandEvent &) {
|
||||
Part *p = PickPart();
|
||||
Part *p = selected_part(tree_);
|
||||
if (!p) p = PickPart();
|
||||
if (!p) return;
|
||||
|
||||
wxFileDialog dlg(this, "Attach a BSDL model", "", "",
|
||||
@@ -416,7 +473,8 @@ void EssimFrame::OnAttachBsdl(wxCommandEvent &) {
|
||||
}
|
||||
|
||||
void EssimFrame::OnConnect(wxCommandEvent &) {
|
||||
Part *p1 = PickPart("Connect — first part");
|
||||
Part *p1 = selected_part(tree_);
|
||||
if (!p1) p1 = PickPart("Connect — first part");
|
||||
if (!p1) return;
|
||||
Part *p2 = PickPart("Connect — second part");
|
||||
if (!p2) return;
|
||||
@@ -453,21 +511,25 @@ void EssimFrame::OnConnect(wxCommandEvent &) {
|
||||
}
|
||||
|
||||
void EssimFrame::OnSetSignalType(wxCommandEvent &) {
|
||||
Module *m = PickModule("Set signal type");
|
||||
if (!m) return;
|
||||
if (m->signals->size() == 0) {
|
||||
wxMessageBox("That module has no signals.", "Set signal type",
|
||||
wxOK | wxICON_INFORMATION, this);
|
||||
return;
|
||||
Module *m = nullptr;
|
||||
Signal *sig = selected_signal(tree_, &m);
|
||||
if (!sig) {
|
||||
m = PickModule("Set signal type");
|
||||
if (!m) return;
|
||||
if (m->signals->size() == 0) {
|
||||
wxMessageBox("That module has no signals.", "Set signal type",
|
||||
wxOK | wxICON_INFORMATION, this);
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> sigs;
|
||||
for (auto &skv : *m->signals) sigs.push_back(skv.first);
|
||||
std::sort(sigs.begin(), sigs.end(), natural_less);
|
||||
wxArrayString schoices;
|
||||
for (const auto &s : sigs) schoices.Add(wx(s));
|
||||
int si = wxGetSingleChoiceIndex("Signal:", "Set signal type", schoices, this);
|
||||
if (si < 0) return;
|
||||
sig = m->signals->get(sigs[si]);
|
||||
}
|
||||
std::vector<std::string> sigs;
|
||||
for (auto &skv : *m->signals) sigs.push_back(skv.first);
|
||||
std::sort(sigs.begin(), sigs.end());
|
||||
wxArrayString schoices;
|
||||
for (const auto &s : sigs) schoices.Add(wx(s));
|
||||
int si = wxGetSingleChoiceIndex("Signal:", "Set signal type", schoices, this);
|
||||
if (si < 0) return;
|
||||
Signal *sig = m->signals->get(sigs[si]);
|
||||
|
||||
static const wxString types[] = {"power", "gnd", "other"};
|
||||
int ti = wxGetSingleChoiceIndex("Type:", "Set signal type",
|
||||
@@ -522,3 +584,22 @@ void EssimFrame::OnAbout(wxCommandEvent &) {
|
||||
"wxWidgets frontend over essim_core.",
|
||||
"About essim", wxOK | wxICON_INFORMATION, this);
|
||||
}
|
||||
|
||||
void EssimFrame::OnTreeContextMenu(wxTreeEvent &ev) {
|
||||
wxTreeItemId id = ev.GetItem();
|
||||
if (id.IsOk()) tree_->SelectItem(id); // the edit handlers read the selection
|
||||
NodeData *d = node_of(tree_, id);
|
||||
if (!d) return;
|
||||
|
||||
// Reuse the menu IDs so these route to the same handlers, which now act on
|
||||
// the (just-selected) tree item.
|
||||
wxMenu menu;
|
||||
if (d->kind == NodeData::Kind::Part || d->kind == NodeData::Kind::Pin) {
|
||||
menu.Append(ID_SET_CONNECTOR_TYPE, "Set connector type…");
|
||||
menu.Append(ID_ATTACH_BSDL, "Attach BSDL…");
|
||||
menu.Append(ID_CONNECT, "Connect to…");
|
||||
} else if (d->kind == NodeData::Kind::Signal) {
|
||||
menu.Append(ID_SET_SIGNAL_TYPE, "Set signal type…");
|
||||
}
|
||||
if (menu.GetMenuItemCount() > 0) PopupMenu(&menu);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user