- Rename tests/wasm-app/ to tests/apps/ for brevity - Move lib/gl_immediate_shim.js to wasm/shims/ (consolidates WASM files) - Delete docs/ directory (outdated Nov 2024 research docs) - Delete patches/ directory (already applied to fork submodules) - Delete wasm/patches/ and wasm/config/ (empty/unused) - Update all file references in scripts, configs, and documentation - Update .gitignore for new tests/apps/ paths Verified both build workflows pass: - wxWidgets tests: 255 passed - Docker KiCad build: completed successfully 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
335 lines
11 KiB
C++
335 lines
11 KiB
C++
// wxListCtrl Virtual Mode Test - Tests virtual list control in WASM
|
|
// KiCad uses virtual mode for large component lists (10000+ items)
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include "wx/notebook.h"
|
|
#include "wx/listctrl.h"
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten/emscripten.h>
|
|
#endif
|
|
|
|
// Virtual list control class
|
|
class VirtualListCtrl : public wxListCtrl
|
|
{
|
|
public:
|
|
VirtualListCtrl(wxWindow* parent, wxWindowID id, int numItems = 10000);
|
|
|
|
virtual wxString OnGetItemText(long item, long column) const override;
|
|
virtual int OnGetItemImage(long item) const override;
|
|
virtual wxListItemAttr* OnGetItemAttr(long item) const override;
|
|
|
|
void SetItemCount(int count);
|
|
int GetTotalItems() const { return m_numItems; }
|
|
|
|
private:
|
|
int m_numItems;
|
|
mutable wxListItemAttr m_attr;
|
|
};
|
|
|
|
VirtualListCtrl::VirtualListCtrl(wxWindow* parent, wxWindowID id, int numItems)
|
|
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
|
|
wxLC_REPORT | wxLC_VIRTUAL | wxLC_SINGLE_SEL)
|
|
, m_numItems(numItems)
|
|
{
|
|
// Set up columns like KiCad component list
|
|
InsertColumn(0, "Reference", wxLIST_FORMAT_LEFT, 100);
|
|
InsertColumn(1, "Value", wxLIST_FORMAT_LEFT, 120);
|
|
InsertColumn(2, "Footprint", wxLIST_FORMAT_LEFT, 180);
|
|
InsertColumn(3, "Qty", wxLIST_FORMAT_CENTER, 50);
|
|
|
|
SetItemCount(m_numItems);
|
|
}
|
|
|
|
wxString VirtualListCtrl::OnGetItemText(long item, long column) const
|
|
{
|
|
switch (column) {
|
|
case 0: // Reference
|
|
if (item % 4 == 0) return wxString::Format("R%ld", item + 1);
|
|
if (item % 4 == 1) return wxString::Format("C%ld", item + 1);
|
|
if (item % 4 == 2) return wxString::Format("U%ld", item + 1);
|
|
return wxString::Format("J%ld", item + 1);
|
|
case 1: // Value
|
|
if (item % 4 == 0) return wxString::Format("%dk", (item % 10) + 1);
|
|
if (item % 4 == 1) return wxString::Format("%dnF", (item % 10 + 1) * 10);
|
|
if (item % 4 == 2) return "STM32F103";
|
|
return "USB-C";
|
|
case 2: // Footprint
|
|
if (item % 4 == 0) return "Resistor_SMD:R_0402";
|
|
if (item % 4 == 1) return "Capacitor_SMD:C_0402";
|
|
if (item % 4 == 2) return "Package_QFP:LQFP-48";
|
|
return "Connector_USB:USB_C";
|
|
case 3: // Qty
|
|
return wxString::Format("%ld", (item % 5) + 1);
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
int VirtualListCtrl::OnGetItemImage(long WXUNUSED(item)) const
|
|
{
|
|
return -1; // No images
|
|
}
|
|
|
|
wxListItemAttr* VirtualListCtrl::OnGetItemAttr(long item) const
|
|
{
|
|
// Alternate row colors like KiCad
|
|
if (item % 2 == 0) {
|
|
m_attr.SetBackgroundColour(wxColour(245, 245, 245));
|
|
} else {
|
|
m_attr.SetBackgroundColour(*wxWHITE);
|
|
}
|
|
return &m_attr;
|
|
}
|
|
|
|
void VirtualListCtrl::SetItemCount(int count)
|
|
{
|
|
m_numItems = count;
|
|
wxListCtrl::SetItemCount(count);
|
|
}
|
|
|
|
// Main application
|
|
class ListCtrlTestApp : public wxApp
|
|
{
|
|
public:
|
|
virtual bool OnInit() override;
|
|
};
|
|
|
|
class ListCtrlTestFrame : public wxFrame
|
|
{
|
|
public:
|
|
ListCtrlTestFrame();
|
|
|
|
private:
|
|
VirtualListCtrl* m_virtualList;
|
|
wxListCtrl* m_normalList;
|
|
wxTextCtrl* m_log;
|
|
wxNotebook* m_notebook;
|
|
wxStaticText* m_itemCountLabel;
|
|
|
|
void LogEvent(const wxString& msg);
|
|
void PopulateNormalList();
|
|
|
|
void OnItemSelected(wxListEvent& evt);
|
|
void OnItemActivated(wxListEvent& evt);
|
|
void OnColumnClick(wxListEvent& evt);
|
|
void OnSetItemCount(wxCommandEvent& evt);
|
|
void OnScrollToItem(wxCommandEvent& evt);
|
|
|
|
wxDECLARE_EVENT_TABLE();
|
|
};
|
|
|
|
enum {
|
|
ID_VIRTUAL_LIST = wxID_HIGHEST + 1,
|
|
ID_NORMAL_LIST,
|
|
ID_SET_COUNT_100,
|
|
ID_SET_COUNT_1000,
|
|
ID_SET_COUNT_10000,
|
|
ID_SET_COUNT_100000,
|
|
ID_SCROLL_TOP,
|
|
ID_SCROLL_MIDDLE,
|
|
ID_SCROLL_BOTTOM
|
|
};
|
|
|
|
wxBEGIN_EVENT_TABLE(ListCtrlTestFrame, wxFrame)
|
|
EVT_LIST_ITEM_SELECTED(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnItemSelected)
|
|
EVT_LIST_ITEM_ACTIVATED(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnItemActivated)
|
|
EVT_LIST_COL_CLICK(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnColumnClick)
|
|
EVT_LIST_ITEM_SELECTED(ID_NORMAL_LIST, ListCtrlTestFrame::OnItemSelected)
|
|
EVT_BUTTON(ID_SET_COUNT_100, ListCtrlTestFrame::OnSetItemCount)
|
|
EVT_BUTTON(ID_SET_COUNT_1000, ListCtrlTestFrame::OnSetItemCount)
|
|
EVT_BUTTON(ID_SET_COUNT_10000, ListCtrlTestFrame::OnSetItemCount)
|
|
EVT_BUTTON(ID_SET_COUNT_100000, ListCtrlTestFrame::OnSetItemCount)
|
|
EVT_BUTTON(ID_SCROLL_TOP, ListCtrlTestFrame::OnScrollToItem)
|
|
EVT_BUTTON(ID_SCROLL_MIDDLE, ListCtrlTestFrame::OnScrollToItem)
|
|
EVT_BUTTON(ID_SCROLL_BOTTOM, ListCtrlTestFrame::OnScrollToItem)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
wxIMPLEMENT_APP(ListCtrlTestApp);
|
|
|
|
bool ListCtrlTestApp::OnInit()
|
|
{
|
|
if (!wxApp::OnInit())
|
|
return false;
|
|
|
|
ListCtrlTestFrame* frame = new ListCtrlTestFrame();
|
|
frame->Show(true);
|
|
return true;
|
|
}
|
|
|
|
ListCtrlTestFrame::ListCtrlTestFrame()
|
|
: wxFrame(nullptr, wxID_ANY, "wxListCtrl Virtual Mode WASM Test",
|
|
wxDefaultPosition, wxSize(800, 700))
|
|
{
|
|
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
|
"wxListCtrl Virtual Mode Test\n\n"
|
|
"KiCad uses virtual mode wxListCtrl for large component lists.\n"
|
|
"Virtual mode only creates items on-demand for visible rows.");
|
|
mainSizer->Add(desc, 0, wxALL, 10);
|
|
|
|
// Controls bar
|
|
wxStaticBoxSizer* ctrlBox = new wxStaticBoxSizer(wxHORIZONTAL, this, "Virtual List Controls");
|
|
|
|
ctrlBox->Add(new wxStaticText(this, wxID_ANY, "Item Count:"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_100, "100"), 0, wxALL, 2);
|
|
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_1000, "1,000"), 0, wxALL, 2);
|
|
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_10000, "10,000"), 0, wxALL, 2);
|
|
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_100000, "100,000"), 0, wxALL, 2);
|
|
|
|
ctrlBox->AddSpacer(20);
|
|
|
|
ctrlBox->Add(new wxStaticText(this, wxID_ANY, "Scroll:"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
ctrlBox->Add(new wxButton(this, ID_SCROLL_TOP, "Top"), 0, wxALL, 2);
|
|
ctrlBox->Add(new wxButton(this, ID_SCROLL_MIDDLE, "Middle"), 0, wxALL, 2);
|
|
ctrlBox->Add(new wxButton(this, ID_SCROLL_BOTTOM, "Bottom"), 0, wxALL, 2);
|
|
|
|
mainSizer->Add(ctrlBox, 0, wxEXPAND | wxALL, 10);
|
|
|
|
// Item count display
|
|
m_itemCountLabel = new wxStaticText(this, wxID_ANY, "Current items: 10,000");
|
|
mainSizer->Add(m_itemCountLabel, 0, wxLEFT, 15);
|
|
|
|
// Notebook with two tabs
|
|
m_notebook = new wxNotebook(this, wxID_ANY);
|
|
|
|
// Tab 1: Virtual List
|
|
wxPanel* virtualPanel = new wxPanel(m_notebook);
|
|
wxBoxSizer* virtualSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_virtualList = new VirtualListCtrl(virtualPanel, ID_VIRTUAL_LIST, 10000);
|
|
virtualSizer->Add(m_virtualList, 1, wxEXPAND | wxALL, 5);
|
|
|
|
virtualPanel->SetSizer(virtualSizer);
|
|
m_notebook->AddPage(virtualPanel, "Virtual List (10,000 items)");
|
|
|
|
// Tab 2: Normal List (for comparison)
|
|
wxPanel* normalPanel = new wxPanel(m_notebook);
|
|
wxBoxSizer* normalSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_normalList = new wxListCtrl(normalPanel, ID_NORMAL_LIST,
|
|
wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
|
|
m_normalList->InsertColumn(0, "Reference", wxLIST_FORMAT_LEFT, 100);
|
|
m_normalList->InsertColumn(1, "Value", wxLIST_FORMAT_LEFT, 120);
|
|
normalSizer->Add(m_normalList, 1, wxEXPAND | wxALL, 5);
|
|
|
|
normalPanel->SetSizer(normalSizer);
|
|
m_notebook->AddPage(normalPanel, "Normal List (100 items)");
|
|
|
|
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 10);
|
|
|
|
// Event log
|
|
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
|
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
|
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
|
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
|
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
|
|
|
SetSizer(mainSizer);
|
|
CreateStatusBar();
|
|
SetStatusText("Ready - Virtual ListCtrl test");
|
|
|
|
PopulateNormalList();
|
|
|
|
LogEvent("ListCtrl test app started");
|
|
LogEvent("Virtual list: 10,000 items (only visible rows created)");
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
EM_ASM({
|
|
console.log('[LISTCTRL_TEST] wxListCtrl virtual mode test app started successfully');
|
|
});
|
|
#endif
|
|
}
|
|
|
|
void ListCtrlTestFrame::PopulateNormalList()
|
|
{
|
|
for (int i = 0; i < 100; i++) {
|
|
long idx = m_normalList->InsertItem(i, wxString::Format("Item %d", i + 1));
|
|
m_normalList->SetItem(idx, 1, wxString::Format("Value %d", i + 1));
|
|
}
|
|
}
|
|
|
|
void ListCtrlTestFrame::LogEvent(const wxString& msg)
|
|
{
|
|
#ifdef __EMSCRIPTEN__
|
|
EM_ASM({
|
|
console.log('[LISTCTRL_EVENT] ' + UTF8ToString($0));
|
|
}, msg.c_str().AsChar());
|
|
#endif
|
|
|
|
if (!m_log)
|
|
return;
|
|
m_log->AppendText(msg + "\n");
|
|
SetStatusText(msg);
|
|
}
|
|
|
|
void ListCtrlTestFrame::OnItemSelected(wxListEvent& evt)
|
|
{
|
|
wxString listName = (evt.GetId() == ID_VIRTUAL_LIST) ? "Virtual" : "Normal";
|
|
LogEvent(wxString::Format("%s list: Selected item %ld", listName, evt.GetIndex()));
|
|
}
|
|
|
|
void ListCtrlTestFrame::OnItemActivated(wxListEvent& evt)
|
|
{
|
|
LogEvent(wxString::Format("Virtual list: Activated item %ld (double-click)", evt.GetIndex()));
|
|
}
|
|
|
|
void ListCtrlTestFrame::OnColumnClick(wxListEvent& evt)
|
|
{
|
|
LogEvent(wxString::Format("Column %d clicked (would sort)", evt.GetColumn()));
|
|
}
|
|
|
|
void ListCtrlTestFrame::OnSetItemCount(wxCommandEvent& evt)
|
|
{
|
|
int count = 0;
|
|
switch (evt.GetId()) {
|
|
case ID_SET_COUNT_100: count = 100; break;
|
|
case ID_SET_COUNT_1000: count = 1000; break;
|
|
case ID_SET_COUNT_10000: count = 10000; break;
|
|
case ID_SET_COUNT_100000: count = 100000; break;
|
|
}
|
|
|
|
m_virtualList->SetItemCount(count);
|
|
|
|
wxString countStr;
|
|
if (count >= 1000) {
|
|
countStr = wxString::Format("%d,%03d", count / 1000, count % 1000);
|
|
} else {
|
|
countStr = wxString::Format("%d", count);
|
|
}
|
|
m_itemCountLabel->SetLabel(wxString::Format("Current items: %s", countStr));
|
|
|
|
// Update tab name
|
|
m_notebook->SetPageText(0, wxString::Format("Virtual List (%s items)", countStr));
|
|
|
|
LogEvent(wxString::Format("Set virtual list to %s items", countStr));
|
|
}
|
|
|
|
void ListCtrlTestFrame::OnScrollToItem(wxCommandEvent& evt)
|
|
{
|
|
int totalItems = m_virtualList->GetTotalItems();
|
|
long targetItem = 0;
|
|
|
|
switch (evt.GetId()) {
|
|
case ID_SCROLL_TOP:
|
|
targetItem = 0;
|
|
break;
|
|
case ID_SCROLL_MIDDLE:
|
|
targetItem = totalItems / 2;
|
|
break;
|
|
case ID_SCROLL_BOTTOM:
|
|
targetItem = totalItems - 1;
|
|
break;
|
|
}
|
|
|
|
m_virtualList->EnsureVisible(targetItem);
|
|
m_virtualList->SetItemState(targetItem, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
|
|
|
LogEvent(wxString::Format("Scrolled to item %ld", targetItem));
|
|
}
|