Reorganize project structure for clarity

- 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>
This commit is contained in:
Viktor Vaczi 2025-12-27 14:06:23 +01:00
commit a26b40c063
78 changed files with 38 additions and 128055 deletions

View file

@ -0,0 +1,143 @@
// wxAuiManager Test - Tests AUI docking functionality in WASM
// KiCad uses AUI extensively for dockable panels
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/aui/aui.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class AuiTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class AuiTestFrame : public wxFrame
{
public:
AuiTestFrame();
~AuiTestFrame();
private:
wxAuiManager m_mgr;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnPaneClose(wxAuiManagerEvent& evt);
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(AuiTestFrame, wxFrame)
EVT_AUI_PANE_CLOSE(AuiTestFrame::OnPaneClose)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(AuiTestApp);
bool AuiTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
AuiTestFrame* frame = new AuiTestFrame();
frame->Show(true);
return true;
}
AuiTestFrame::AuiTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxAuiManager WASM Test",
wxDefaultPosition, wxSize(800, 600))
{
m_mgr.SetManagedWindow(this);
// Create center pane with event log
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
m_mgr.AddPane(m_log, wxAuiPaneInfo().Name("log").Caption("Event Log")
.Center().CloseButton(false));
// Create left panel (like KiCad's properties panel)
wxPanel* leftPanel = new wxPanel(this);
leftPanel->SetBackgroundColour(*wxLIGHT_GREY);
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "Properties Panel"), 0, wxALL, 10);
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "Like KiCad's"), 0, wxALL, 5);
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "property editor"), 0, wxALL, 5);
leftPanel->SetSizer(leftSizer);
m_mgr.AddPane(leftPanel, wxAuiPaneInfo().Name("properties").Caption("Properties")
.Left().Layer(1).Position(1).CloseButton(true).MaximizeButton(true)
.MinSize(150, 200));
// Create right panel (like KiCad's layer manager)
wxPanel* rightPanel = new wxPanel(this);
rightPanel->SetBackgroundColour(wxColour(240, 240, 255));
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "Layers Panel"), 0, wxALL, 10);
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "Like KiCad's"), 0, wxALL, 5);
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "layer manager"), 0, wxALL, 5);
rightPanel->SetSizer(rightSizer);
m_mgr.AddPane(rightPanel, wxAuiPaneInfo().Name("layers").Caption("Layers")
.Right().Layer(1).Position(1).CloseButton(true).MaximizeButton(true)
.MinSize(150, 200));
// Create bottom panel (like KiCad's message panel)
wxPanel* bottomPanel = new wxPanel(this);
bottomPanel->SetBackgroundColour(wxColour(255, 255, 240));
wxBoxSizer* bottomSizer = new wxBoxSizer(wxVERTICAL);
bottomSizer->Add(new wxStaticText(bottomPanel, wxID_ANY,
"Message Panel - Like KiCad's message area"), 0, wxALL, 5);
bottomPanel->SetSizer(bottomSizer);
m_mgr.AddPane(bottomPanel, wxAuiPaneInfo().Name("messages").Caption("Messages")
.Bottom().Layer(0).Position(1).CloseButton(true)
.MinSize(-1, 80));
m_mgr.Update();
CreateStatusBar();
SetStatusText("AUI Manager ready - try dragging and docking panels");
LogEvent("AUI test app started");
LogEvent("Created dockable panels: Properties, Layers, Messages");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[AUI_TEST] wxAuiManager test app started successfully');
});
#endif
}
AuiTestFrame::~AuiTestFrame()
{
m_mgr.UnInit();
}
void AuiTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[AUI_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void AuiTestFrame::OnPaneClose(wxAuiManagerEvent& evt)
{
wxAuiPaneInfo* pane = evt.GetPane();
if (pane) {
LogEvent(wxString::Format("Pane closing: %s", pane->name));
}
}

View file

@ -0,0 +1,244 @@
// wxAuiNotebook Test - Tab panels for KiCad editors
// Tests wxAuiNotebook for dockable, closeable tab panels
#include "wx/wx.h"
#include "wx/aui/auibook.h"
#include "wx/aui/aui.h"
#include "wx/textctrl.h"
class AuiNotebookFrame : public wxFrame
{
public:
AuiNotebookFrame() : wxFrame(nullptr, wxID_ANY, "wxAuiNotebook Test",
wxDefaultPosition, wxSize(1000, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxAuiNotebook for tabbed panels in some editors.\n"
"Features: closeable tabs, reorderable tabs, split views.");
mainSizer->Add(desc, 0, wxALL, 5);
// Controls
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* btnAdd = new wxButton(mainPanel, wxID_ANY, "Add Tab");
wxButton* btnRemove = new wxButton(mainPanel, wxID_ANY, "Remove Tab");
wxButton* btnSplit = new wxButton(mainPanel, wxID_ANY, "Split View");
btnAdd->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnAddTab, this);
btnRemove->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnRemoveTab, this);
btnSplit->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnSplitView, this);
controlSizer->Add(btnAdd, 0, wxRIGHT, 5);
controlSizer->Add(btnRemove, 0, wxRIGHT, 5);
controlSizer->Add(btnSplit, 0, wxRIGHT, 20);
// Style options
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Tab Style:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
wxButton* btnTop = new wxButton(mainPanel, wxID_ANY, "Top");
wxButton* btnBottom = new wxButton(mainPanel, wxID_ANY, "Bottom");
btnTop->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetTabPosition(wxAUI_NB_TOP); });
btnBottom->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetTabPosition(wxAUI_NB_BOTTOM); });
controlSizer->Add(btnTop, 0, wxRIGHT, 2);
controlSizer->Add(btnBottom, 0);
mainSizer->Add(controlSizer, 0, wxALL, 5);
// AuiNotebook
m_notebook = new wxAuiNotebook(mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxAUI_NB_DEFAULT_STYLE | wxAUI_NB_TAB_EXTERNAL_MOVE | wxAUI_NB_CLOSE_ON_ALL_TABS);
// Create initial tabs like KiCad editor panels
CreateSchematicTab();
CreatePCBTab();
CreateSymbolTab();
CreateFootprintTab();
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Bind notebook events
m_notebook->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, &AuiNotebookFrame::OnPageChanged, this);
m_notebook->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, &AuiNotebookFrame::OnPageClose, this);
m_notebook->Bind(wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN, &AuiNotebookFrame::OnTabRightClick, this);
// Status bar
CreateStatusBar();
SetStatusText("AuiNotebook test app started");
Log("AuiNotebook test app started");
Log("4 tabs created: Schematic, PCB, Symbol, Footprint");
m_tabCounter = 5;
}
private:
void CreateSchematicTab()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(panel, wxID_ANY, "Schematic Editor"), 0, wxALL, 10);
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Schematic Editor tab."), 0, wxLEFT, 10);
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
"Components:\n- U1: STM32F103\n- R1-R10: 10k Resistors\n- C1-C5: 100nF Capacitors\n- J1: USB Connector",
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Schematic", true);
}
void CreatePCBTab()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(panel, wxID_ANY, "PCB Editor"), 0, wxALL, 10);
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad PCB Editor tab."), 0, wxLEFT, 10);
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
"Board Info:\n- Size: 100mm x 80mm\n- Layers: 4 (F.Cu, In1.Cu, In2.Cu, B.Cu)\n- Track Width: 0.25mm\n- Via Size: 0.8mm",
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "PCB", false);
}
void CreateSymbolTab()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(panel, wxID_ANY, "Symbol Editor"), 0, wxALL, 10);
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Symbol Editor tab."), 0, wxLEFT, 10);
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
"Symbol: STM32F103\n- Pins: 48\n- Units: 4 (A, B, C, D)\n- Power Pins: VCC, GND\n- Library: MCU_ST_STM32",
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Symbol", false);
}
void CreateFootprintTab()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(panel, wxID_ANY, "Footprint Editor"), 0, wxALL, 10);
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Footprint Editor tab."), 0, wxLEFT, 10);
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
"Footprint: LQFP-48\n- Pads: 48\n- Pitch: 0.5mm\n- Package: 7x7mm\n- Library: Package_QFP",
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Footprint", false);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void OnAddTab(wxCommandEvent& event)
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxString tabName = wxString::Format("Tab %d", m_tabCounter++);
sizer->Add(new wxStaticText(panel, wxID_ANY, tabName), 0, wxALL, 10);
sizer->Add(new wxStaticText(panel, wxID_ANY, "New dynamically created tab"), 0, wxLEFT, 10);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, tabName, true);
Log(wxString::Format("Added new tab: %s", tabName));
}
void OnRemoveTab(wxCommandEvent& event)
{
int selection = m_notebook->GetSelection();
if (selection != wxNOT_FOUND && m_notebook->GetPageCount() > 1)
{
wxString tabName = m_notebook->GetPageText(selection);
m_notebook->DeletePage(selection);
Log(wxString::Format("Removed tab: %s", tabName));
}
}
void OnSplitView(wxCommandEvent& event)
{
// wxAuiNotebook supports split views natively
Log("Split view requested (drag tab to edge to split)");
}
void SetTabPosition(int style)
{
long currentStyle = m_notebook->GetWindowStyleFlag();
currentStyle &= ~(wxAUI_NB_TOP | wxAUI_NB_BOTTOM);
currentStyle |= style;
m_notebook->SetWindowStyleFlag(currentStyle);
m_notebook->Refresh();
Log(style == wxAUI_NB_TOP ? "Tabs moved to top" : "Tabs moved to bottom");
}
void OnPageChanged(wxAuiNotebookEvent& event)
{
int sel = event.GetSelection();
if (sel != wxNOT_FOUND)
{
Log(wxString::Format("Tab changed to: %s", m_notebook->GetPageText(sel)));
}
}
void OnPageClose(wxAuiNotebookEvent& event)
{
int sel = event.GetSelection();
if (sel != wxNOT_FOUND)
{
Log(wxString::Format("Tab closing: %s", m_notebook->GetPageText(sel)));
}
}
void OnTabRightClick(wxAuiNotebookEvent& event)
{
Log("Tab right-clicked (context menu would appear)");
}
wxAuiNotebook* m_notebook;
wxTextCtrl* m_log;
int m_tabCounter;
};
class AuiNotebookApp : public wxApp
{
public:
virtual bool OnInit() override
{
AuiNotebookFrame* frame = new AuiNotebookFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(AuiNotebookApp);

View file

@ -0,0 +1,280 @@
// wxBitmapButton Test - Custom bitmap buttons like KiCad's toolbar
// Tests wxBitmapButton, disabled states, different icon shapes
#include "wx/wx.h"
#include "wx/dcmemory.h"
#include "wx/artprov.h"
// Helper to create simple bitmap icons
wxBitmap CreateIcon(const wxColour& color, int size = 24, const wxString& shape = "rect")
{
wxBitmap bmp(size, size);
wxMemoryDC dc(bmp);
dc.SetBackground(wxBrush(wxColour(240, 240, 240)));
dc.Clear();
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(wxBrush(color));
if (shape == "circle")
{
dc.DrawCircle(size / 2, size / 2, size / 2 - 2);
}
else if (shape == "triangle")
{
wxPoint points[3] = {
wxPoint(size / 2, 2),
wxPoint(2, size - 2),
wxPoint(size - 2, size - 2)
};
dc.DrawPolygon(3, points);
}
else if (shape == "diamond")
{
wxPoint points[4] = {
wxPoint(size / 2, 2),
wxPoint(2, size / 2),
wxPoint(size / 2, size - 2),
wxPoint(size - 2, size / 2)
};
dc.DrawPolygon(4, points);
}
else // rect
{
dc.DrawRectangle(2, 2, size - 4, size - 4);
}
return bmp;
}
// Create a "tool" icon with multiple elements
wxBitmap CreateToolIcon(const wxColour& mainColor, const wxString& symbol)
{
int size = 24;
wxBitmap bmp(size, size);
wxMemoryDC dc(bmp);
dc.SetBackground(wxBrush(wxColour(240, 240, 240)));
dc.Clear();
// Draw main shape
dc.SetPen(wxPen(mainColor, 2));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(3, 3, 18, 18);
// Draw symbol
dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.SetTextForeground(mainColor);
wxSize textSize = dc.GetTextExtent(symbol);
dc.DrawText(symbol, (size - textSize.x) / 2, (size - textSize.y) / 2);
return bmp;
}
class BitmapButtonsFrame : public wxFrame
{
public:
BitmapButtonsFrame() : wxFrame(nullptr, wxID_ANY, "wxBitmapButton Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxBitmapButton extensively for toolbars and dialogs.\n"
"Tests: Bitmap buttons, disabled states, icon buttons.");
mainSizer->Add(desc, 0, wxALL, 5);
// Toolbar-style buttons
wxStaticBoxSizer* toolbarSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Toolbar Style");
m_btnSelect = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxBLACK, "S"));
m_btnSelect->SetToolTip("Select Tool");
m_btnLine = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxBLUE, "L"));
m_btnLine->SetToolTip("Line Tool");
m_btnRect = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(wxColour(0, 128, 0), "R"));
m_btnRect->SetToolTip("Rectangle Tool");
m_btnCircle = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxRED, "C"));
m_btnCircle->SetToolTip("Circle Tool");
m_btnText = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(wxColour(128, 0, 128), "T"));
m_btnText->SetToolTip("Text Tool");
m_btnSelect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Select tool clicked"); });
m_btnLine->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Line tool clicked"); });
m_btnRect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Rectangle tool clicked"); });
m_btnCircle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Circle tool clicked"); });
m_btnText->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Text tool clicked"); });
toolbarSizer->Add(m_btnSelect, 0, wxALL, 2);
toolbarSizer->Add(m_btnLine, 0, wxALL, 2);
toolbarSizer->Add(m_btnRect, 0, wxALL, 2);
toolbarSizer->Add(m_btnCircle, 0, wxALL, 2);
toolbarSizer->Add(m_btnText, 0, wxALL, 2);
mainSizer->Add(toolbarSizer, 0, wxEXPAND | wxALL, 5);
// Layer visibility buttons (simulated with checkboxes)
wxStaticBoxSizer* layerSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Layer Visibility (Toggle Buttons)");
m_chkFCu = new wxCheckBox(mainPanel, wxID_ANY, "F.Cu");
m_chkFCu->SetValue(true);
m_chkBCu = new wxCheckBox(mainPanel, wxID_ANY, "B.Cu");
m_chkBCu->SetValue(true);
m_chkSilk = new wxCheckBox(mainPanel, wxID_ANY, "Silk");
m_chkSilk->SetValue(true);
m_chkMask = new wxCheckBox(mainPanel, wxID_ANY, "Mask");
m_chkMask->SetValue(false);
m_chkFCu->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
Log(wxString::Format("F.Cu toggle: %s", e.IsChecked() ? "ON" : "OFF"));
});
m_chkBCu->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
Log(wxString::Format("B.Cu toggle: %s", e.IsChecked() ? "ON" : "OFF"));
});
m_chkSilk->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
Log(wxString::Format("Silk toggle: %s", e.IsChecked() ? "ON" : "OFF"));
});
m_chkMask->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
Log(wxString::Format("Mask toggle: %s", e.IsChecked() ? "ON" : "OFF"));
});
layerSizer->Add(m_chkFCu, 0, wxALL, 5);
layerSizer->Add(m_chkBCu, 0, wxALL, 5);
layerSizer->Add(m_chkSilk, 0, wxALL, 5);
layerSizer->Add(m_chkMask, 0, wxALL, 5);
mainSizer->Add(layerSizer, 0, wxEXPAND | wxALL, 5);
// Disabled state buttons
wxStaticBoxSizer* disabledSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Disabled State");
m_btnEnabled = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(0, 128, 0), 24, "circle"));
m_btnEnabled->SetToolTip("Enabled button");
m_btnDisabled = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(128, 128, 128), 24, "circle"));
m_btnDisabled->Enable(false);
m_btnDisabled->SetToolTip("Disabled button");
wxButton* btnToggleEnabled = new wxButton(mainPanel, wxID_ANY, "Toggle Enable State");
btnToggleEnabled->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
m_btnDisabled->Enable(!m_btnDisabled->IsEnabled());
Log(wxString::Format("Button enabled: %s", m_btnDisabled->IsEnabled() ? "Yes" : "No"));
});
disabledSizer->Add(m_btnEnabled, 0, wxALL, 5);
disabledSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Enabled"), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
disabledSizer->AddSpacer(20);
disabledSizer->Add(m_btnDisabled, 0, wxALL, 5);
disabledSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Disabled"), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
disabledSizer->AddSpacer(20);
disabledSizer->Add(btnToggleEnabled, 0, wxALL, 5);
mainSizer->Add(disabledSizer, 0, wxEXPAND | wxALL, 5);
// Different shapes
wxStaticBoxSizer* shapesSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Different Icon Shapes");
wxBitmapButton* btnRect = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(*wxRED, 32, "rect"));
wxBitmapButton* btnCircle = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(*wxBLUE, 32, "circle"));
wxBitmapButton* btnTriangle = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(0, 128, 0), 32, "triangle"));
wxBitmapButton* btnDiamond = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(128, 0, 128), 32, "diamond"));
btnRect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Rectangle shape clicked"); });
btnCircle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Circle shape clicked"); });
btnTriangle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Triangle shape clicked"); });
btnDiamond->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Diamond shape clicked"); });
shapesSizer->Add(btnRect, 0, wxALL, 5);
shapesSizer->Add(btnCircle, 0, wxALL, 5);
shapesSizer->Add(btnTriangle, 0, wxALL, 5);
shapesSizer->Add(btnDiamond, 0, wxALL, 5);
mainSizer->Add(shapesSizer, 0, wxEXPAND | wxALL, 5);
// Art Provider buttons
wxStaticBoxSizer* artSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Art Provider Icons");
wxBitmapButton* btnNew = new wxBitmapButton(mainPanel, wxID_ANY,
wxArtProvider::GetBitmap(wxART_NEW, wxART_TOOLBAR));
wxBitmapButton* btnOpen = new wxBitmapButton(mainPanel, wxID_ANY,
wxArtProvider::GetBitmap(wxART_FILE_OPEN, wxART_TOOLBAR));
wxBitmapButton* btnSave = new wxBitmapButton(mainPanel, wxID_ANY,
wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_TOOLBAR));
wxBitmapButton* btnUndo = new wxBitmapButton(mainPanel, wxID_ANY,
wxArtProvider::GetBitmap(wxART_UNDO, wxART_TOOLBAR));
wxBitmapButton* btnRedo = new wxBitmapButton(mainPanel, wxID_ANY,
wxArtProvider::GetBitmap(wxART_REDO, wxART_TOOLBAR));
btnNew->SetToolTip("New");
btnOpen->SetToolTip("Open");
btnSave->SetToolTip("Save");
btnUndo->SetToolTip("Undo");
btnRedo->SetToolTip("Redo");
btnNew->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("New clicked"); });
btnOpen->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Open clicked"); });
btnSave->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Save clicked"); });
btnUndo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Undo clicked"); });
btnRedo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Redo clicked"); });
artSizer->Add(btnNew, 0, wxALL, 2);
artSizer->Add(btnOpen, 0, wxALL, 2);
artSizer->Add(btnSave, 0, wxALL, 2);
artSizer->AddSpacer(10);
artSizer->Add(btnUndo, 0, wxALL, 2);
artSizer->Add(btnRedo, 0, wxALL, 2);
mainSizer->Add(artSizer, 0, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Bitmap buttons test app started");
Log("Bitmap buttons test app started");
}
private:
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxBitmapButton* m_btnSelect;
wxBitmapButton* m_btnLine;
wxBitmapButton* m_btnRect;
wxBitmapButton* m_btnCircle;
wxBitmapButton* m_btnText;
wxCheckBox* m_chkFCu;
wxCheckBox* m_chkBCu;
wxCheckBox* m_chkSilk;
wxCheckBox* m_chkMask;
wxBitmapButton* m_btnEnabled;
wxBitmapButton* m_btnDisabled;
wxTextCtrl* m_log;
};
class BitmapButtonsApp : public wxApp
{
public:
virtual bool OnInit() override
{
BitmapButtonsFrame* frame = new BitmapButtonsFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(BitmapButtonsApp);

View file

@ -0,0 +1,206 @@
// Bitmap Masking Test - Tests wxBitmap with wxMask for transparency
// Tests: wxMask, wxBitmap::SetMask(), wxDC::DrawBitmap() with useMask=true
#include "wx/wx.h"
#include "wx/dcmemory.h"
class BitmaskFrame : public wxFrame
{
public:
BitmaskFrame() : wxFrame(nullptr, wxID_ANY, "Bitmap Masking Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"Tests bitmap masking for transparency.\n"
"Uses wxMask with color-keyed transparency.");
mainSizer->Add(desc, 0, wxALL, 5);
// Drawing panel
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 450));
m_drawPanel->SetBackgroundColour(*wxWHITE);
m_drawPanel->Bind(wxEVT_PAINT, &BitmaskFrame::OnPaint, this);
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Create test bitmaps
CreateTestBitmaps();
CreateStatusBar();
SetStatusText("Bitmap masking test app started");
Log("Bitmap masking test app started");
Log("Testing wxMask with color-keyed transparency");
}
private:
void CreateTestBitmaps()
{
// Create a bitmap with magenta (255,0,255) as transparent color
m_bitmapWithMask.Create(80, 80, 32);
wxMemoryDC dc(m_bitmapWithMask);
// Fill with magenta (will become transparent)
dc.SetBackground(wxBrush(wxColour(255, 0, 255)));
dc.Clear();
// Draw a blue circle in the center
dc.SetBrush(wxBrush(wxColour(0, 0, 255)));
dc.SetPen(wxPen(wxColour(0, 0, 128), 2));
dc.DrawCircle(40, 40, 30);
dc.SelectObject(wxNullBitmap);
// Set the mask using magenta as transparent color
m_bitmapWithMask.SetMask(new wxMask(m_bitmapWithMask, wxColour(255, 0, 255)));
Log("Created 80x80 bitmap with magenta mask");
// Create a bitmap with green as transparent color
m_bitmapGreenMask.Create(80, 80, 32);
wxMemoryDC dc2(m_bitmapGreenMask);
// Fill with green (will become transparent)
dc2.SetBackground(wxBrush(wxColour(0, 255, 0)));
dc2.Clear();
// Draw a red rectangle
dc2.SetBrush(wxBrush(wxColour(255, 0, 0)));
dc2.SetPen(wxPen(wxColour(128, 0, 0), 2));
dc2.DrawRectangle(15, 15, 50, 50);
dc2.SelectObject(wxNullBitmap);
m_bitmapGreenMask.SetMask(new wxMask(m_bitmapGreenMask, wxColour(0, 255, 0)));
Log("Created 80x80 bitmap with green mask");
// Create a bitmap without mask for comparison
m_bitmapNoMask.Create(80, 80, 32);
wxMemoryDC dc3(m_bitmapNoMask);
dc3.SetBackground(wxBrush(wxColour(255, 0, 255)));
dc3.Clear();
dc3.SetBrush(wxBrush(wxColour(0, 0, 255)));
dc3.SetPen(wxPen(wxColour(0, 0, 128), 2));
dc3.DrawCircle(40, 40, 30);
dc3.SelectObject(wxNullBitmap);
Log("Created 80x80 bitmap without mask");
// Create a checkerboard pattern bitmap with mask
m_bitmapCheckerboard.Create(80, 80, 32);
wxMemoryDC dc4(m_bitmapCheckerboard);
dc4.SetBackground(wxBrush(wxColour(255, 0, 255)));
dc4.Clear();
// Draw checkerboard
for (int y = 0; y < 80; y += 20)
{
for (int x = 0; x < 80; x += 20)
{
if ((x / 20 + y / 20) % 2 == 0)
{
dc4.SetBrush(wxBrush(wxColour(50, 50, 50)));
dc4.SetPen(*wxTRANSPARENT_PEN);
dc4.DrawRectangle(x, y, 20, 20);
}
}
}
dc4.SelectObject(wxNullBitmap);
m_bitmapCheckerboard.SetMask(new wxMask(m_bitmapCheckerboard, wxColour(255, 0, 255)));
Log("Created 80x80 checkerboard bitmap with mask");
}
void OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(m_drawPanel);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
int y = 20;
// Section 1: Show bitmap without mask
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.DrawText("No Mask (magenta visible):", 20, y);
dc.DrawBitmap(m_bitmapNoMask, 300, y - 10, false);
y += 100;
// Section 2: Show bitmap with mask
dc.DrawText("With Mask (magenta transparent):", 20, y);
dc.DrawBitmap(m_bitmapWithMask, 300, y - 10, true);
y += 100;
// Section 3: Show green mask bitmap
dc.DrawText("Green Mask (green transparent):", 20, y);
dc.DrawBitmap(m_bitmapGreenMask, 300, y - 10, true);
y += 100;
// Section 4: Show on colored background to verify transparency
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc.DrawText("Masked bitmaps on colored backgrounds:", 20, y);
y += 25;
// Yellow background
dc.SetBrush(wxBrush(wxColour(255, 255, 0)));
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(100, y, 100, 100);
dc.DrawBitmap(m_bitmapWithMask, 110, y + 10, true);
// Cyan background
dc.SetBrush(wxBrush(wxColour(0, 255, 255)));
dc.DrawRectangle(220, y, 100, 100);
dc.DrawBitmap(m_bitmapGreenMask, 230, y + 10, true);
// Gray background with checkerboard
dc.SetBrush(wxBrush(wxColour(200, 200, 200)));
dc.DrawRectangle(340, y, 100, 100);
dc.DrawBitmap(m_bitmapCheckerboard, 350, y + 10, true);
// Gradient-like striped background
for (int i = 0; i < 100; i += 10)
{
dc.SetBrush(wxBrush(wxColour(100 + i, 50, 150 - i)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(460 + i, y, 10, 100);
}
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(460, y, 100, 100);
dc.DrawBitmap(m_bitmapWithMask, 470, y + 10, true);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxPanel* m_drawPanel;
wxTextCtrl* m_log;
wxBitmap m_bitmapWithMask;
wxBitmap m_bitmapNoMask;
wxBitmap m_bitmapGreenMask;
wxBitmap m_bitmapCheckerboard;
};
class BitmaskApp : public wxApp
{
public:
virtual bool OnInit() override
{
BitmaskFrame* frame = new BitmaskFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(BitmaskApp);

View file

@ -0,0 +1,213 @@
// wxCalendarCtrl Test - Date selection
// Tests wxCalendarCtrl for date picking functionality
#include "wx/wx.h"
#include "wx/calctrl.h"
#include "wx/datectrl.h"
#include "wx/dateevt.h"
class CalendarFrame : public wxFrame
{
public:
CalendarFrame() : wxFrame(nullptr, wxID_ANY, "wxCalendarCtrl Test",
wxDefaultPosition, wxSize(700, 550))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad may use wxCalendarCtrl for date-related features.\n"
"This tests calendar widget rendering and date selection.");
mainSizer->Add(desc, 0, wxALL, 5);
// Horizontal layout for calendar and controls
wxBoxSizer* hSizer = new wxBoxSizer(wxHORIZONTAL);
// Calendar control
wxStaticBoxSizer* calBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Calendar");
m_calendar = new wxCalendarCtrl(mainPanel, wxID_ANY, wxDefaultDateTime,
wxDefaultPosition, wxDefaultSize,
wxCAL_SHOW_HOLIDAYS | wxCAL_SHOW_SURROUNDING_WEEKS);
calBox->Add(m_calendar, 1, wxEXPAND | wxALL, 5);
hSizer->Add(calBox, 1, wxEXPAND | wxRIGHT, 10);
// Controls panel
wxBoxSizer* controlSizer = new wxBoxSizer(wxVERTICAL);
// Date picker
wxStaticBoxSizer* pickerBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Date Picker");
m_datePicker = new wxDatePickerCtrl(mainPanel, wxID_ANY);
pickerBox->Add(m_datePicker, 0, wxEXPAND | wxALL, 5);
controlSizer->Add(pickerBox, 0, wxEXPAND | wxBOTTOM, 10);
// Navigation buttons
wxStaticBoxSizer* navBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Navigation");
wxButton* btnToday = new wxButton(mainPanel, wxID_ANY, "Today");
wxButton* btnPrevMonth = new wxButton(mainPanel, wxID_ANY, "Previous Month");
wxButton* btnNextMonth = new wxButton(mainPanel, wxID_ANY, "Next Month");
wxButton* btnPrevYear = new wxButton(mainPanel, wxID_ANY, "Previous Year");
wxButton* btnNextYear = new wxButton(mainPanel, wxID_ANY, "Next Year");
btnToday->Bind(wxEVT_BUTTON, &CalendarFrame::OnToday, this);
btnPrevMonth->Bind(wxEVT_BUTTON, &CalendarFrame::OnPrevMonth, this);
btnNextMonth->Bind(wxEVT_BUTTON, &CalendarFrame::OnNextMonth, this);
btnPrevYear->Bind(wxEVT_BUTTON, &CalendarFrame::OnPrevYear, this);
btnNextYear->Bind(wxEVT_BUTTON, &CalendarFrame::OnNextYear, this);
navBox->Add(btnToday, 0, wxEXPAND | wxALL, 2);
navBox->Add(btnPrevMonth, 0, wxEXPAND | wxALL, 2);
navBox->Add(btnNextMonth, 0, wxEXPAND | wxALL, 2);
navBox->Add(btnPrevYear, 0, wxEXPAND | wxALL, 2);
navBox->Add(btnNextYear, 0, wxEXPAND | wxALL, 2);
controlSizer->Add(navBox, 0, wxEXPAND | wxBOTTOM, 10);
// Selected date display
wxStaticBoxSizer* selBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Selected Date");
m_selectedLabel = new wxStaticText(mainPanel, wxID_ANY, "No date selected");
selBox->Add(m_selectedLabel, 0, wxALL, 5);
controlSizer->Add(selBox, 0, wxEXPAND);
hSizer->Add(controlSizer, 0, wxEXPAND);
mainSizer->Add(hSizer, 0, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 120),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Bind calendar events
m_calendar->Bind(wxEVT_CALENDAR_SEL_CHANGED, &CalendarFrame::OnDateChanged, this);
m_calendar->Bind(wxEVT_CALENDAR_DAY_CHANGED, &CalendarFrame::OnDayChanged, this);
m_calendar->Bind(wxEVT_CALENDAR_MONTH_CHANGED, &CalendarFrame::OnMonthChanged, this);
m_calendar->Bind(wxEVT_CALENDAR_YEAR_CHANGED, &CalendarFrame::OnYearChanged, this);
m_calendar->Bind(wxEVT_CALENDAR_DOUBLECLICKED, &CalendarFrame::OnDoubleClicked, this);
m_datePicker->Bind(wxEVT_DATE_CHANGED, &CalendarFrame::OnPickerChanged, this);
// Status bar
CreateStatusBar();
SetStatusText("Calendar test app started");
Log("Calendar test app started");
UpdateSelectedLabel();
}
private:
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void UpdateSelectedLabel()
{
wxDateTime date = m_calendar->GetDate();
m_selectedLabel->SetLabel(date.FormatDate());
}
void OnDateChanged(wxCalendarEvent& event)
{
wxDateTime date = event.GetDate();
Log(wxString::Format("Date selected: %s", date.FormatDate()));
UpdateSelectedLabel();
m_datePicker->SetValue(date);
}
void OnDayChanged(wxCalendarEvent& event)
{
Log(wxString::Format("Day changed: %d", event.GetDate().GetDay()));
}
void OnMonthChanged(wxCalendarEvent& event)
{
wxDateTime date = event.GetDate();
Log(wxString::Format("Month changed: %s %d",
wxDateTime::GetMonthName(date.GetMonth()), date.GetYear()));
}
void OnYearChanged(wxCalendarEvent& event)
{
Log(wxString::Format("Year changed: %d", event.GetDate().GetYear()));
}
void OnDoubleClicked(wxCalendarEvent& event)
{
wxDateTime date = event.GetDate();
Log(wxString::Format("Double-clicked: %s", date.FormatDate()));
}
void OnPickerChanged(wxDateEvent& event)
{
wxDateTime date = event.GetDate();
m_calendar->SetDate(date);
Log(wxString::Format("Date picker changed: %s", date.FormatDate()));
UpdateSelectedLabel();
}
void OnToday(wxCommandEvent& event)
{
m_calendar->SetDate(wxDateTime::Today());
m_datePicker->SetValue(wxDateTime::Today());
Log("Navigated to today");
UpdateSelectedLabel();
}
void OnPrevMonth(wxCommandEvent& event)
{
wxDateTime date = m_calendar->GetDate();
date -= wxDateSpan::Month();
m_calendar->SetDate(date);
Log(wxString::Format("Previous month: %s", date.FormatDate()));
UpdateSelectedLabel();
}
void OnNextMonth(wxCommandEvent& event)
{
wxDateTime date = m_calendar->GetDate();
date += wxDateSpan::Month();
m_calendar->SetDate(date);
Log(wxString::Format("Next month: %s", date.FormatDate()));
UpdateSelectedLabel();
}
void OnPrevYear(wxCommandEvent& event)
{
wxDateTime date = m_calendar->GetDate();
date -= wxDateSpan::Year();
m_calendar->SetDate(date);
Log(wxString::Format("Previous year: %d", date.GetYear()));
UpdateSelectedLabel();
}
void OnNextYear(wxCommandEvent& event)
{
wxDateTime date = m_calendar->GetDate();
date += wxDateSpan::Year();
m_calendar->SetDate(date);
Log(wxString::Format("Next year: %d", date.GetYear()));
UpdateSelectedLabel();
}
wxCalendarCtrl* m_calendar;
wxDatePickerCtrl* m_datePicker;
wxStaticText* m_selectedLabel;
wxTextCtrl* m_log;
};
class CalendarApp : public wxApp
{
public:
virtual bool OnInit() override
{
CalendarFrame* frame = new CalendarFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(CalendarApp);

View file

@ -0,0 +1,213 @@
// wxClipboard Test - Tests clipboard functionality in WASM
// KiCad uses clipboard for copy/paste of schematic symbols, PCB components, text
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/clipbrd.h"
#include "wx/dataobj.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class ClipboardTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class ClipboardTestFrame : public wxFrame
{
public:
ClipboardTestFrame();
private:
wxTextCtrl* m_input;
wxTextCtrl* m_output;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnCopyText(wxCommandEvent& evt);
void OnPasteText(wxCommandEvent& evt);
void OnClearClipboard(wxCommandEvent& evt);
void OnCheckClipboard(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_COPY_TEXT = wxID_HIGHEST + 1,
ID_PASTE_TEXT,
ID_CLEAR_CLIPBOARD,
ID_CHECK_CLIPBOARD
};
wxBEGIN_EVENT_TABLE(ClipboardTestFrame, wxFrame)
EVT_BUTTON(ID_COPY_TEXT, ClipboardTestFrame::OnCopyText)
EVT_BUTTON(ID_PASTE_TEXT, ClipboardTestFrame::OnPasteText)
EVT_BUTTON(ID_CLEAR_CLIPBOARD, ClipboardTestFrame::OnClearClipboard)
EVT_BUTTON(ID_CHECK_CLIPBOARD, ClipboardTestFrame::OnCheckClipboard)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(ClipboardTestApp);
bool ClipboardTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
ClipboardTestFrame* frame = new ClipboardTestFrame();
frame->Show(true);
return true;
}
ClipboardTestFrame::ClipboardTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxClipboard WASM Test",
wxDefaultPosition, wxSize(600, 500))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxClipboard Test\n\n"
"Tests clipboard operations that KiCad uses for copy/paste.\n"
"Note: Browser clipboard access may be restricted.");
mainSizer->Add(desc, 0, wxALL, 10);
// Input section
wxStaticBoxSizer* inputBox = new wxStaticBoxSizer(wxVERTICAL, this, "Text to Copy");
m_input = new wxTextCtrl(this, wxID_ANY, "Sample text for clipboard test",
wxDefaultPosition, wxSize(-1, 60), wxTE_MULTILINE);
inputBox->Add(m_input, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(inputBox, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
// Buttons
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
buttonSizer->Add(new wxButton(this, ID_COPY_TEXT, "Copy to Clipboard"), 0, wxALL, 5);
buttonSizer->Add(new wxButton(this, ID_PASTE_TEXT, "Paste from Clipboard"), 0, wxALL, 5);
buttonSizer->Add(new wxButton(this, ID_CHECK_CLIPBOARD, "Check Clipboard"), 0, wxALL, 5);
buttonSizer->Add(new wxButton(this, ID_CLEAR_CLIPBOARD, "Clear Clipboard"), 0, wxALL, 5);
mainSizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxALL, 10);
// Output section
wxStaticBoxSizer* outputBox = new wxStaticBoxSizer(wxVERTICAL, this, "Pasted Text");
m_output = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 60), wxTE_MULTILINE | wxTE_READONLY);
outputBox->Add(m_output, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(outputBox, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
// Log section
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, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
// Status bar
CreateStatusBar();
SetStatusText("Ready - Test clipboard operations");
LogEvent("Clipboard test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[CLIPBOARD_TEST] wxClipboard test app started successfully');
});
#endif
}
void ClipboardTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[CLIPBOARD_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void ClipboardTestFrame::OnCopyText(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Attempting to copy text to clipboard...");
wxString text = m_input->GetValue();
if (text.IsEmpty()) {
LogEvent("ERROR: No text to copy");
return;
}
if (wxTheClipboard->Open()) {
wxTheClipboard->SetData(new wxTextDataObject(text));
wxTheClipboard->Close();
LogEvent(wxString::Format("SUCCESS: Copied %d characters to clipboard", (int)text.Length()));
} else {
LogEvent("ERROR: Could not open clipboard");
}
}
void ClipboardTestFrame::OnPasteText(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Attempting to paste from clipboard...");
if (wxTheClipboard->Open()) {
if (wxTheClipboard->IsSupported(wxDF_TEXT) ||
wxTheClipboard->IsSupported(wxDF_UNICODETEXT)) {
wxTextDataObject data;
wxTheClipboard->GetData(data);
wxString text = data.GetText();
m_output->SetValue(text);
LogEvent(wxString::Format("SUCCESS: Pasted %d characters from clipboard", (int)text.Length()));
} else {
LogEvent("WARNING: No text data in clipboard");
m_output->SetValue("");
}
wxTheClipboard->Close();
} else {
LogEvent("ERROR: Could not open clipboard");
}
}
void ClipboardTestFrame::OnClearClipboard(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Attempting to clear clipboard...");
if (wxTheClipboard->Open()) {
wxTheClipboard->Clear();
wxTheClipboard->Close();
LogEvent("SUCCESS: Clipboard cleared");
} else {
LogEvent("ERROR: Could not open clipboard");
}
}
void ClipboardTestFrame::OnCheckClipboard(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Checking clipboard contents...");
if (wxTheClipboard->Open()) {
bool hasText = wxTheClipboard->IsSupported(wxDF_TEXT) ||
wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
bool hasBitmap = wxTheClipboard->IsSupported(wxDF_BITMAP);
bool hasFiles = wxTheClipboard->IsSupported(wxDF_FILENAME);
wxString status = "Clipboard contains: ";
if (hasText) status += "TEXT ";
if (hasBitmap) status += "BITMAP ";
if (hasFiles) status += "FILES ";
if (!hasText && !hasBitmap && !hasFiles) status += "(empty or unsupported format)";
LogEvent(status);
wxTheClipboard->Close();
} else {
LogEvent("ERROR: Could not open clipboard");
}
}

View file

@ -0,0 +1,252 @@
// wxCollapsiblePane Test - Tests collapsible pane in WASM
// KiCad uses collapsible panes for property panel grouping
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/collpane.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class CollapsibleTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class CollapsibleTestFrame : public wxFrame
{
public:
CollapsibleTestFrame();
private:
wxCollapsiblePane* m_pane1;
wxCollapsiblePane* m_pane2;
wxCollapsiblePane* m_pane3;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void CreatePaneContents(wxCollapsiblePane* pane, const wxString& type);
void OnPaneChanged(wxCollapsiblePaneEvent& evt);
void OnExpandAll(wxCommandEvent& evt);
void OnCollapseAll(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_PANE_1 = wxID_HIGHEST + 1,
ID_PANE_2,
ID_PANE_3,
ID_EXPAND_ALL,
ID_COLLAPSE_ALL
};
wxBEGIN_EVENT_TABLE(CollapsibleTestFrame, wxFrame)
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_1, CollapsibleTestFrame::OnPaneChanged)
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_2, CollapsibleTestFrame::OnPaneChanged)
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_3, CollapsibleTestFrame::OnPaneChanged)
EVT_BUTTON(ID_EXPAND_ALL, CollapsibleTestFrame::OnExpandAll)
EVT_BUTTON(ID_COLLAPSE_ALL, CollapsibleTestFrame::OnCollapseAll)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(CollapsibleTestApp);
bool CollapsibleTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
CollapsibleTestFrame* frame = new CollapsibleTestFrame();
frame->Show(true);
return true;
}
CollapsibleTestFrame::CollapsibleTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxCollapsiblePane WASM Test",
wxDefaultPosition, wxSize(600, 700))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxCollapsiblePane Test\n\n"
"KiCad uses collapsible panes for grouping properties in property panels.\n"
"Click the arrows to expand/collapse each section.");
mainSizer->Add(desc, 0, wxALL, 10);
// Button bar
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_EXPAND_ALL, "Expand All"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_COLLAPSE_ALL, "Collapse All"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
// Scrolled window to hold collapsible panes
wxScrolledWindow* scrollWin = new wxScrolledWindow(this, wxID_ANY,
wxDefaultPosition, wxDefaultSize, wxVSCROLL);
scrollWin->SetScrollRate(0, 10);
wxBoxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
// Collapsible pane 1: General Properties (like KiCad component properties)
m_pane1 = new wxCollapsiblePane(scrollWin, ID_PANE_1, "General Properties");
CreatePaneContents(m_pane1, "general");
scrollSizer->Add(m_pane1, 0, wxEXPAND | wxALL, 5);
// Collapsible pane 2: Position Properties
m_pane2 = new wxCollapsiblePane(scrollWin, ID_PANE_2, "Position & Orientation");
CreatePaneContents(m_pane2, "position");
scrollSizer->Add(m_pane2, 0, wxEXPAND | wxALL, 5);
// Collapsible pane 3: Display Properties
m_pane3 = new wxCollapsiblePane(scrollWin, ID_PANE_3, "Display Options");
CreatePaneContents(m_pane3, "display");
scrollSizer->Add(m_pane3, 0, wxEXPAND | wxALL, 5);
scrollWin->SetSizer(scrollSizer);
mainSizer->Add(scrollWin, 1, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready - wxCollapsiblePane test");
// Expand first pane by default
m_pane1->Expand();
LogEvent("CollapsiblePane test app started");
LogEvent("3 collapsible sections created");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[COLLAPSIBLE_TEST] wxCollapsiblePane test app started successfully');
});
#endif
}
void CollapsibleTestFrame::CreatePaneContents(wxCollapsiblePane* pane, const wxString& type)
{
wxWindow* paneWin = pane->GetPane();
wxBoxSizer* paneSizer = new wxBoxSizer(wxVERTICAL);
if (type == "general") {
// KiCad-like component properties
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 10, 10);
grid->AddGrowableCol(1);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Reference:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "R1"), 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Value:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "10k"), 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Footprint:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "Resistor_SMD:R_0402"), 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Datasheet:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "~"), 1, wxEXPAND);
paneSizer->Add(grid, 0, wxEXPAND | wxALL, 10);
}
else if (type == "position") {
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 10, 10);
grid->AddGrowableCol(1);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "X Position:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "100.5 mm"), 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Y Position:"), 0, wxALIGN_CENTER_VERTICAL);
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "50.25 mm"), 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Rotation:"), 0, wxALIGN_CENTER_VERTICAL);
wxChoice* rotChoice = new wxChoice(paneWin, wxID_ANY);
rotChoice->Append("0°");
rotChoice->Append("90°");
rotChoice->Append("180°");
rotChoice->Append("270°");
rotChoice->SetSelection(1);
grid->Add(rotChoice, 1, wxEXPAND);
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Side:"), 0, wxALIGN_CENTER_VERTICAL);
wxChoice* sideChoice = new wxChoice(paneWin, wxID_ANY);
sideChoice->Append("Front");
sideChoice->Append("Back");
sideChoice->SetSelection(0);
grid->Add(sideChoice, 1, wxEXPAND);
paneSizer->Add(grid, 0, wxEXPAND | wxALL, 10);
}
else if (type == "display") {
wxBoxSizer* checkSizer = new wxBoxSizer(wxVERTICAL);
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Reference"), 0, wxALL, 5);
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Value"), 0, wxALL, 5);
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Footprint"), 0, wxALL, 5);
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Highlight on Selection"), 0, wxALL, 5);
paneSizer->Add(checkSizer, 0, wxEXPAND | wxALL, 5);
}
paneWin->SetSizer(paneSizer);
}
void CollapsibleTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[COLLAPSIBLE_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void CollapsibleTestFrame::OnPaneChanged(wxCollapsiblePaneEvent& evt)
{
wxString paneName;
switch (evt.GetId()) {
case ID_PANE_1: paneName = "General Properties"; break;
case ID_PANE_2: paneName = "Position & Orientation"; break;
case ID_PANE_3: paneName = "Display Options"; break;
default: paneName = "Unknown"; break;
}
LogEvent(wxString::Format("Pane '%s' %s",
paneName,
evt.GetCollapsed() ? "collapsed" : "expanded"));
// Relayout the parent
Layout();
}
void CollapsibleTestFrame::OnExpandAll(wxCommandEvent& WXUNUSED(evt))
{
m_pane1->Expand();
m_pane2->Expand();
m_pane3->Expand();
Layout();
LogEvent("All panes expanded");
}
void CollapsibleTestFrame::OnCollapseAll(wxCommandEvent& WXUNUSED(evt))
{
m_pane1->Collapse();
m_pane2->Collapse();
m_pane3->Collapse();
Layout();
LogEvent("All panes collapsed");
}

View file

@ -0,0 +1,459 @@
// wxDataViewCtrl Test - Tests DataViewCtrl in WASM
// KiCad uses DataViewCtrl for Zone Manager, Net Inspector, Library browsers
// This is CRITICAL for KiCad functionality
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/dataview.h"
#include "wx/notebook.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class DataViewTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class DataViewTestFrame : public wxFrame
{
public:
DataViewTestFrame();
private:
wxDataViewListCtrl* m_listCtrl;
wxDataViewTreeCtrl* m_treeCtrl;
wxTextCtrl* m_log;
wxNotebook* m_notebook;
void LogEvent(const wxString& msg);
void PopulateList();
void PopulateTree();
// List events
void OnListSelectionChanged(wxDataViewEvent& evt);
void OnListItemActivated(wxDataViewEvent& evt);
void OnListColumnHeaderClick(wxDataViewEvent& evt);
void OnListItemStartEditing(wxDataViewEvent& evt);
void OnListItemEditingDone(wxDataViewEvent& evt);
// Tree events
void OnTreeSelectionChanged(wxDataViewEvent& evt);
void OnTreeItemExpanding(wxDataViewEvent& evt);
void OnTreeItemCollapsing(wxDataViewEvent& evt);
void OnTreeItemActivated(wxDataViewEvent& evt);
// Button handlers
void OnAddListItem(wxCommandEvent& evt);
void OnRemoveListItem(wxCommandEvent& evt);
void OnClearList(wxCommandEvent& evt);
void OnExpandTree(wxCommandEvent& evt);
void OnCollapseTree(wxCommandEvent& evt);
void OnAddTreeItem(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_LIST = wxID_HIGHEST + 1,
ID_TREE,
ID_ADD_LIST_ITEM,
ID_REMOVE_LIST_ITEM,
ID_CLEAR_LIST,
ID_EXPAND_TREE,
ID_COLLAPSE_TREE,
ID_ADD_TREE_ITEM
};
wxBEGIN_EVENT_TABLE(DataViewTestFrame, wxFrame)
// List events
EVT_DATAVIEW_SELECTION_CHANGED(ID_LIST, DataViewTestFrame::OnListSelectionChanged)
EVT_DATAVIEW_ITEM_ACTIVATED(ID_LIST, DataViewTestFrame::OnListItemActivated)
EVT_DATAVIEW_COLUMN_HEADER_CLICK(ID_LIST, DataViewTestFrame::OnListColumnHeaderClick)
EVT_DATAVIEW_ITEM_START_EDITING(ID_LIST, DataViewTestFrame::OnListItemStartEditing)
EVT_DATAVIEW_ITEM_EDITING_DONE(ID_LIST, DataViewTestFrame::OnListItemEditingDone)
// Tree events
EVT_DATAVIEW_SELECTION_CHANGED(ID_TREE, DataViewTestFrame::OnTreeSelectionChanged)
EVT_DATAVIEW_ITEM_EXPANDING(ID_TREE, DataViewTestFrame::OnTreeItemExpanding)
EVT_DATAVIEW_ITEM_COLLAPSING(ID_TREE, DataViewTestFrame::OnTreeItemCollapsing)
EVT_DATAVIEW_ITEM_ACTIVATED(ID_TREE, DataViewTestFrame::OnTreeItemActivated)
// Buttons
EVT_BUTTON(ID_ADD_LIST_ITEM, DataViewTestFrame::OnAddListItem)
EVT_BUTTON(ID_REMOVE_LIST_ITEM, DataViewTestFrame::OnRemoveListItem)
EVT_BUTTON(ID_CLEAR_LIST, DataViewTestFrame::OnClearList)
EVT_BUTTON(ID_EXPAND_TREE, DataViewTestFrame::OnExpandTree)
EVT_BUTTON(ID_COLLAPSE_TREE, DataViewTestFrame::OnCollapseTree)
EVT_BUTTON(ID_ADD_TREE_ITEM, DataViewTestFrame::OnAddTreeItem)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(DataViewTestApp);
bool DataViewTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
DataViewTestFrame* frame = new DataViewTestFrame();
frame->Show(true);
return true;
}
DataViewTestFrame::DataViewTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxDataViewCtrl WASM Test",
wxDefaultPosition, wxSize(800, 700))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxDataViewCtrl Test\n\n"
"KiCad uses DataViewCtrl for Zone Manager, Net Inspector, and Library browsers.\n"
"Test both list and tree views.");
mainSizer->Add(desc, 0, wxALL, 10);
// Notebook for list and tree tabs
m_notebook = new wxNotebook(this, wxID_ANY);
// === List Tab ===
wxPanel* listPanel = new wxPanel(m_notebook);
wxBoxSizer* listSizer = new wxBoxSizer(wxVERTICAL);
// List button bar
wxBoxSizer* listBtnSizer = new wxBoxSizer(wxHORIZONTAL);
listBtnSizer->Add(new wxButton(listPanel, ID_ADD_LIST_ITEM, "Add Item"), 0, wxALL, 5);
listBtnSizer->Add(new wxButton(listPanel, ID_REMOVE_LIST_ITEM, "Remove Selected"), 0, wxALL, 5);
listBtnSizer->Add(new wxButton(listPanel, ID_CLEAR_LIST, "Clear All"), 0, wxALL, 5);
listSizer->Add(listBtnSizer, 0, wxALIGN_CENTER);
// DataViewListCtrl - like KiCad Zone Manager
m_listCtrl = new wxDataViewListCtrl(listPanel, ID_LIST, wxDefaultPosition, wxSize(-1, 200));
// Add columns similar to KiCad Zone Manager
m_listCtrl->AppendTextColumn("Zone Name", wxDATAVIEW_CELL_EDITABLE, 150);
m_listCtrl->AppendTextColumn("Net", wxDATAVIEW_CELL_INERT, 100);
m_listCtrl->AppendTextColumn("Layer", wxDATAVIEW_CELL_INERT, 80);
m_listCtrl->AppendTextColumn("Priority", wxDATAVIEW_CELL_EDITABLE, 60);
m_listCtrl->AppendTextColumn("Fill Mode", wxDATAVIEW_CELL_INERT, 80);
listSizer->Add(m_listCtrl, 1, wxEXPAND | wxALL, 10);
listPanel->SetSizer(listSizer);
m_notebook->AddPage(listPanel, "List View");
// === Tree Tab ===
wxPanel* treePanel = new wxPanel(m_notebook);
wxBoxSizer* treeSizer = new wxBoxSizer(wxVERTICAL);
// Tree button bar
wxBoxSizer* treeBtnSizer = new wxBoxSizer(wxHORIZONTAL);
treeBtnSizer->Add(new wxButton(treePanel, ID_EXPAND_TREE, "Expand All"), 0, wxALL, 5);
treeBtnSizer->Add(new wxButton(treePanel, ID_COLLAPSE_TREE, "Collapse All"), 0, wxALL, 5);
treeBtnSizer->Add(new wxButton(treePanel, ID_ADD_TREE_ITEM, "Add Item"), 0, wxALL, 5);
treeSizer->Add(treeBtnSizer, 0, wxALIGN_CENTER);
// DataViewTreeCtrl - like KiCad Library Browser
m_treeCtrl = new wxDataViewTreeCtrl(treePanel, ID_TREE, wxDefaultPosition, wxSize(-1, 200));
treeSizer->Add(m_treeCtrl, 1, wxEXPAND | wxALL, 10);
treePanel->SetSizer(treeSizer);
m_notebook->AddPage(treePanel, "Tree View");
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
// Populate controls
PopulateList();
PopulateTree();
LogEvent("DataViewCtrl test app started");
LogEvent("List populated with KiCad Zone Manager-like data");
LogEvent("Tree populated with KiCad Library-like hierarchy");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DATAVIEW_TEST] wxDataViewCtrl test app started successfully');
});
#endif
}
void DataViewTestFrame::PopulateList()
{
// Add Zone Manager-like data
wxVector<wxVariant> data;
data.clear();
data.push_back(wxVariant("Zone_GND_Top"));
data.push_back(wxVariant("GND"));
data.push_back(wxVariant("F.Cu"));
data.push_back(wxVariant("0"));
data.push_back(wxVariant("Solid"));
m_listCtrl->AppendItem(data);
data.clear();
data.push_back(wxVariant("Zone_GND_Bottom"));
data.push_back(wxVariant("GND"));
data.push_back(wxVariant("B.Cu"));
data.push_back(wxVariant("0"));
data.push_back(wxVariant("Solid"));
m_listCtrl->AppendItem(data);
data.clear();
data.push_back(wxVariant("Zone_VCC"));
data.push_back(wxVariant("VCC"));
data.push_back(wxVariant("F.Cu"));
data.push_back(wxVariant("1"));
data.push_back(wxVariant("Hatched"));
m_listCtrl->AppendItem(data);
data.clear();
data.push_back(wxVariant("Zone_3V3"));
data.push_back(wxVariant("3V3"));
data.push_back(wxVariant("B.Cu"));
data.push_back(wxVariant("2"));
data.push_back(wxVariant("Hatched"));
m_listCtrl->AppendItem(data);
data.clear();
data.push_back(wxVariant("Zone_Shield"));
data.push_back(wxVariant("GND"));
data.push_back(wxVariant("Edge.Cuts"));
data.push_back(wxVariant("3"));
data.push_back(wxVariant("Solid"));
m_listCtrl->AppendItem(data);
// Add more items for virtual scrolling test
for (int i = 1; i <= 20; i++) {
data.clear();
data.push_back(wxVariant(wxString::Format("Zone_Custom_%d", i)));
data.push_back(wxVariant(wxString::Format("Net_%d", i)));
data.push_back(wxVariant("In1.Cu"));
data.push_back(wxVariant(wxString::Format("%d", i + 3)));
data.push_back(wxVariant("Solid"));
m_listCtrl->AppendItem(data);
}
}
void DataViewTestFrame::PopulateTree()
{
// Create Library Browser-like hierarchy
wxDataViewItem root = m_treeCtrl->AppendContainer(wxDataViewItem(), "Libraries");
// Symbol Libraries
wxDataViewItem symbols = m_treeCtrl->AppendContainer(root, "Symbol Libraries");
wxDataViewItem device = m_treeCtrl->AppendContainer(symbols, "Device");
m_treeCtrl->AppendItem(device, "R - Resistor");
m_treeCtrl->AppendItem(device, "C - Capacitor");
m_treeCtrl->AppendItem(device, "L - Inductor");
m_treeCtrl->AppendItem(device, "D - Diode");
m_treeCtrl->AppendItem(device, "LED");
wxDataViewItem connector = m_treeCtrl->AppendContainer(symbols, "Connector");
m_treeCtrl->AppendItem(connector, "Conn_01x02");
m_treeCtrl->AppendItem(connector, "Conn_01x04");
m_treeCtrl->AppendItem(connector, "USB_B");
m_treeCtrl->AppendItem(connector, "USB_C");
wxDataViewItem mcu = m_treeCtrl->AppendContainer(symbols, "MCU_ST");
m_treeCtrl->AppendItem(mcu, "STM32F103C8");
m_treeCtrl->AppendItem(mcu, "STM32F401RE");
m_treeCtrl->AppendItem(mcu, "STM32G431KB");
// Footprint Libraries
wxDataViewItem footprints = m_treeCtrl->AppendContainer(root, "Footprint Libraries");
wxDataViewItem resistors = m_treeCtrl->AppendContainer(footprints, "Resistor_SMD");
m_treeCtrl->AppendItem(resistors, "R_0402");
m_treeCtrl->AppendItem(resistors, "R_0603");
m_treeCtrl->AppendItem(resistors, "R_0805");
m_treeCtrl->AppendItem(resistors, "R_1206");
wxDataViewItem capacitors = m_treeCtrl->AppendContainer(footprints, "Capacitor_SMD");
m_treeCtrl->AppendItem(capacitors, "C_0402");
m_treeCtrl->AppendItem(capacitors, "C_0603");
m_treeCtrl->AppendItem(capacitors, "C_0805");
// Expand root
m_treeCtrl->Expand(root);
}
void DataViewTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DATAVIEW_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
// List event handlers
void DataViewTestFrame::OnListSelectionChanged(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
int row = m_listCtrl->ItemToRow(item);
wxVariant val;
m_listCtrl->GetValue(val, row, 0);
LogEvent(wxString::Format("List: Selection changed to row %d: '%s'", row, val.GetString()));
}
}
void DataViewTestFrame::OnListItemActivated(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
int row = m_listCtrl->ItemToRow(item);
wxVariant val;
m_listCtrl->GetValue(val, row, 0);
LogEvent(wxString::Format("List: Item activated (double-click) row %d: '%s'", row, val.GetString()));
}
}
void DataViewTestFrame::OnListColumnHeaderClick(wxDataViewEvent& evt)
{
int col = evt.GetColumn();
wxString colName = m_listCtrl->GetColumn(col)->GetTitle();
LogEvent(wxString::Format("List: Column header clicked: '%s' (col %d)", colName, col));
}
void DataViewTestFrame::OnListItemStartEditing(wxDataViewEvent& evt)
{
int row = m_listCtrl->ItemToRow(evt.GetItem());
int col = evt.GetColumn();
LogEvent(wxString::Format("List: Start editing row %d, col %d", row, col));
}
void DataViewTestFrame::OnListItemEditingDone(wxDataViewEvent& evt)
{
int row = m_listCtrl->ItemToRow(evt.GetItem());
int col = evt.GetColumn();
wxString newVal = evt.GetValue().GetString();
LogEvent(wxString::Format("List: Editing done row %d, col %d, new value: '%s'", row, col, newVal));
}
// Tree event handlers
void DataViewTestFrame::OnTreeSelectionChanged(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
wxString text = m_treeCtrl->GetItemText(item);
LogEvent(wxString::Format("Tree: Selection changed to '%s'", text));
}
}
void DataViewTestFrame::OnTreeItemExpanding(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
wxString text = m_treeCtrl->GetItemText(item);
LogEvent(wxString::Format("Tree: Expanding '%s'", text));
}
}
void DataViewTestFrame::OnTreeItemCollapsing(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
wxString text = m_treeCtrl->GetItemText(item);
LogEvent(wxString::Format("Tree: Collapsing '%s'", text));
}
}
void DataViewTestFrame::OnTreeItemActivated(wxDataViewEvent& evt)
{
wxDataViewItem item = evt.GetItem();
if (item.IsOk()) {
wxString text = m_treeCtrl->GetItemText(item);
LogEvent(wxString::Format("Tree: Item activated (double-click) '%s'", text));
}
}
// Button handlers
void DataViewTestFrame::OnAddListItem(wxCommandEvent& WXUNUSED(evt))
{
static int itemNum = 1;
wxVector<wxVariant> data;
data.push_back(wxVariant(wxString::Format("New_Zone_%d", itemNum)));
data.push_back(wxVariant("NewNet"));
data.push_back(wxVariant("F.Cu"));
data.push_back(wxVariant(wxString::Format("%d", itemNum)));
data.push_back(wxVariant("Solid"));
m_listCtrl->AppendItem(data);
LogEvent(wxString::Format("List: Added new item 'New_Zone_%d'", itemNum));
itemNum++;
}
void DataViewTestFrame::OnRemoveListItem(wxCommandEvent& WXUNUSED(evt))
{
int row = m_listCtrl->GetSelectedRow();
if (row != wxNOT_FOUND) {
wxVariant val;
m_listCtrl->GetValue(val, row, 0);
m_listCtrl->DeleteItem(row);
LogEvent(wxString::Format("List: Removed item '%s' at row %d", val.GetString(), row));
} else {
LogEvent("List: No item selected to remove");
}
}
void DataViewTestFrame::OnClearList(wxCommandEvent& WXUNUSED(evt))
{
m_listCtrl->DeleteAllItems();
LogEvent("List: All items cleared");
}
void DataViewTestFrame::OnExpandTree(wxCommandEvent& WXUNUSED(evt))
{
// Expand all items by iterating
wxDataViewItemArray children;
m_treeCtrl->GetStore()->GetChildren(wxDataViewItem(), children);
for (size_t i = 0; i < children.GetCount(); i++) {
m_treeCtrl->Expand(children[i]);
wxDataViewItemArray subChildren;
m_treeCtrl->GetStore()->GetChildren(children[i], subChildren);
for (size_t j = 0; j < subChildren.GetCount(); j++) {
m_treeCtrl->Expand(subChildren[j]);
}
}
LogEvent("Tree: All items expanded");
}
void DataViewTestFrame::OnCollapseTree(wxCommandEvent& WXUNUSED(evt))
{
wxDataViewItemArray children;
m_treeCtrl->GetStore()->GetChildren(wxDataViewItem(), children);
for (size_t i = 0; i < children.GetCount(); i++) {
m_treeCtrl->Collapse(children[i]);
}
LogEvent("Tree: All items collapsed");
}
void DataViewTestFrame::OnAddTreeItem(wxCommandEvent& WXUNUSED(evt))
{
wxDataViewItem sel = m_treeCtrl->GetSelection();
if (sel.IsOk()) {
static int itemNum = 1;
m_treeCtrl->AppendItem(sel, wxString::Format("New Item %d", itemNum++));
m_treeCtrl->Expand(sel);
LogEvent(wxString::Format("Tree: Added new item under '%s'", m_treeCtrl->GetItemText(sel)));
} else {
LogEvent("Tree: No item selected - select a parent first");
}
}

View file

@ -0,0 +1,306 @@
// wxDataViewCtrl Virtual Mode Test - Zone Manager/Net Inspector simulation
// Tests wxDataViewCtrl with virtual data model for large datasets
#include "wx/wx.h"
#include "wx/dataview.h"
#include "wx/splitter.h"
// Virtual data model for large datasets (like Zone Manager or Net Inspector)
class VirtualNetModel : public wxDataViewVirtualListModel
{
public:
VirtualNetModel(int itemCount = 10000) : m_itemCount(itemCount)
{
// Pre-generate some sample data patterns
m_netClasses = {"Default", "Power", "Signal", "Clock", "Differential"};
}
virtual unsigned int GetColumnCount() const override { return 4; }
virtual wxString GetColumnType(unsigned int col) const override
{
return "string";
}
virtual unsigned int GetCount() const override { return m_itemCount; }
virtual void GetValueByRow(wxVariant& variant, unsigned int row, unsigned int col) const override
{
switch (col)
{
case 0: // Net Name
variant = wxString::Format("NET_%05d", row);
break;
case 1: // Net Class
variant = m_netClasses[row % m_netClasses.size()];
break;
case 2: // Connection Count
variant = wxString::Format("%d", (row * 7 + 3) % 50);
break;
case 3: // Length (mm)
variant = wxString::Format("%.2f", (row * 13 + 5) % 1000 / 10.0);
break;
}
}
virtual bool SetValueByRow(const wxVariant& variant, unsigned int row, unsigned int col) override
{
return false; // Read-only for this test
}
void SetItemCount(int count)
{
m_itemCount = count;
Reset(count);
}
private:
int m_itemCount;
std::vector<wxString> m_netClasses;
};
// Zone model for Zone Manager simulation
class VirtualZoneModel : public wxDataViewVirtualListModel
{
public:
VirtualZoneModel(int itemCount = 1000) : m_itemCount(itemCount)
{
m_layers = {"F.Cu", "B.Cu", "In1.Cu", "In2.Cu"};
m_priorities = {"0", "1", "2", "3"};
}
virtual unsigned int GetColumnCount() const override { return 5; }
virtual wxString GetColumnType(unsigned int col) const override
{
return "string";
}
virtual unsigned int GetCount() const override { return m_itemCount; }
virtual void GetValueByRow(wxVariant& variant, unsigned int row, unsigned int col) const override
{
switch (col)
{
case 0: // Zone Name
variant = wxString::Format("Zone_%03d", row);
break;
case 1: // Net
variant = wxString::Format("NET_%04d", row % 500);
break;
case 2: // Layer
variant = m_layers[row % m_layers.size()];
break;
case 3: // Priority
variant = m_priorities[row % m_priorities.size()];
break;
case 4: // Area (mm²)
variant = wxString::Format("%.1f", (row * 17 + 100) % 5000 / 10.0);
break;
}
}
virtual bool SetValueByRow(const wxVariant& variant, unsigned int row, unsigned int col) override
{
return false;
}
void SetItemCount(int count)
{
m_itemCount = count;
Reset(count);
}
private:
int m_itemCount;
std::vector<wxString> m_layers;
std::vector<wxString> m_priorities;
};
class DataViewVirtualFrame : public wxFrame
{
public:
DataViewVirtualFrame() : wxFrame(nullptr, wxID_ANY, "wxDataViewCtrl Virtual Mode Test",
wxDefaultPosition, wxSize(1200, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxDataViewCtrl with virtual models for Zone Manager and Net Inspector.\n"
"Virtual mode handles 10,000+ items efficiently by only creating visible rows.");
mainSizer->Add(desc, 0, wxALL, 5);
// Controls
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Item Count:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
wxButton* btn100 = new wxButton(mainPanel, wxID_ANY, "100");
wxButton* btn1000 = new wxButton(mainPanel, wxID_ANY, "1,000");
wxButton* btn10000 = new wxButton(mainPanel, wxID_ANY, "10,000");
wxButton* btn50000 = new wxButton(mainPanel, wxID_ANY, "50,000");
btn100->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(100); });
btn1000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(1000); });
btn10000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(10000); });
btn50000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(50000); });
controlSizer->Add(btn100, 0, wxRIGHT, 2);
controlSizer->Add(btn1000, 0, wxRIGHT, 2);
controlSizer->Add(btn10000, 0, wxRIGHT, 2);
controlSizer->Add(btn50000, 0, wxRIGHT, 10);
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Scroll:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
wxButton* btnTop = new wxButton(mainPanel, wxID_ANY, "Top");
wxButton* btnMiddle = new wxButton(mainPanel, wxID_ANY, "Middle");
wxButton* btnBottom = new wxButton(mainPanel, wxID_ANY, "Bottom");
btnTop->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(0); });
btnMiddle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(m_netModel->GetCount() / 2); });
btnBottom->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(m_netModel->GetCount() - 1); });
controlSizer->Add(btnTop, 0, wxRIGHT, 2);
controlSizer->Add(btnMiddle, 0, wxRIGHT, 2);
controlSizer->Add(btnBottom, 0);
mainSizer->Add(controlSizer, 0, wxALL, 5);
// Count label
m_countLabel = new wxStaticText(mainPanel, wxID_ANY, "Current items: 10,000");
mainSizer->Add(m_countLabel, 0, wxLEFT | wxBOTTOM, 5);
// Splitter for two DataViewCtrls
wxSplitterWindow* splitter = new wxSplitterWindow(mainPanel, wxID_ANY);
// Net Inspector panel (left)
wxPanel* netPanel = new wxPanel(splitter);
wxBoxSizer* netSizer = new wxBoxSizer(wxVERTICAL);
netSizer->Add(new wxStaticText(netPanel, wxID_ANY, "Net Inspector (Virtual List - 10,000 items)"), 0, wxBOTTOM, 5);
m_netView = new wxDataViewCtrl(netPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxDV_ROW_LINES | wxDV_VERT_RULES);
m_netModel = new VirtualNetModel(10000);
m_netView->AssociateModel(m_netModel);
m_netModel->DecRef(); // Model is now owned by the view
m_netView->AppendTextColumn("Net Name", 0, wxDATAVIEW_CELL_INERT, 120);
m_netView->AppendTextColumn("Net Class", 1, wxDATAVIEW_CELL_INERT, 100);
m_netView->AppendTextColumn("Connections", 2, wxDATAVIEW_CELL_INERT, 100);
m_netView->AppendTextColumn("Length (mm)", 3, wxDATAVIEW_CELL_INERT, 100);
netSizer->Add(m_netView, 1, wxEXPAND);
netPanel->SetSizer(netSizer);
// Zone Manager panel (right)
wxPanel* zonePanel = new wxPanel(splitter);
wxBoxSizer* zoneSizer = new wxBoxSizer(wxVERTICAL);
zoneSizer->Add(new wxStaticText(zonePanel, wxID_ANY, "Zone Manager (Virtual List - 1,000 items)"), 0, wxBOTTOM, 5);
m_zoneView = new wxDataViewCtrl(zonePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxDV_ROW_LINES | wxDV_VERT_RULES);
m_zoneModel = new VirtualZoneModel(1000);
m_zoneView->AssociateModel(m_zoneModel);
m_zoneModel->DecRef();
m_zoneView->AppendTextColumn("Zone", 0, wxDATAVIEW_CELL_INERT, 80);
m_zoneView->AppendTextColumn("Net", 1, wxDATAVIEW_CELL_INERT, 100);
m_zoneView->AppendTextColumn("Layer", 2, wxDATAVIEW_CELL_INERT, 80);
m_zoneView->AppendTextColumn("Priority", 3, wxDATAVIEW_CELL_INERT, 70);
m_zoneView->AppendTextColumn("Area (mm²)", 4, wxDATAVIEW_CELL_INERT, 90);
zoneSizer->Add(m_zoneView, 1, wxEXPAND);
zonePanel->SetSizer(zoneSizer);
splitter->SplitVertically(netPanel, zonePanel, 500);
mainSizer->Add(splitter, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Bind selection events
m_netView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &DataViewVirtualFrame::OnNetSelected, this);
m_zoneView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &DataViewVirtualFrame::OnZoneSelected, this);
// Status bar
CreateStatusBar();
SetStatusText("DataViewCtrl virtual mode test app started");
Log("DataViewVirtual test app started");
Log("Net Inspector: 10,000 virtual items");
Log("Zone Manager: 1,000 virtual items");
}
private:
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void SetNetCount(int count)
{
m_netModel->SetItemCount(count);
m_countLabel->SetLabel(wxString::Format("Current items: %d", count));
Log(wxString::Format("Net Inspector set to %d items", count));
}
void ScrollTo(int row)
{
wxDataViewItem item = m_netModel->GetItem(row);
m_netView->EnsureVisible(item);
Log(wxString::Format("Scrolled to row %d", row));
}
void OnNetSelected(wxDataViewEvent& event)
{
wxDataViewItem item = event.GetItem();
if (item.IsOk())
{
int row = m_netModel->GetRow(item);
wxVariant val;
m_netModel->GetValueByRow(val, row, 0);
Log(wxString::Format("Net selected: %s (row %d)", val.GetString(), row));
}
}
void OnZoneSelected(wxDataViewEvent& event)
{
wxDataViewItem item = event.GetItem();
if (item.IsOk())
{
int row = m_zoneModel->GetRow(item);
wxVariant val;
m_zoneModel->GetValueByRow(val, row, 0);
Log(wxString::Format("Zone selected: %s (row %d)", val.GetString(), row));
}
}
wxDataViewCtrl* m_netView;
wxDataViewCtrl* m_zoneView;
VirtualNetModel* m_netModel;
VirtualZoneModel* m_zoneModel;
wxStaticText* m_countLabel;
wxTextCtrl* m_log;
};
class DataViewVirtualApp : public wxApp
{
public:
virtual bool OnInit() override
{
DataViewVirtualFrame* frame = new DataViewVirtualFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(DataViewVirtualApp);

View file

@ -0,0 +1,225 @@
// wxDialog/wxMessageBox Test - Tests modal dialogs in WASM
// KiCad uses dialogs for alerts, confirmations, and custom dialogs
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class DialogTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class DialogTestFrame : public wxFrame
{
public:
DialogTestFrame();
private:
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnInfoDialog(wxCommandEvent& evt);
void OnYesNoDialog(wxCommandEvent& evt);
void OnErrorDialog(wxCommandEvent& evt);
void OnCustomDialog(wxCommandEvent& evt);
void OnInputDialog(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
// Custom dialog class (like KiCad's property dialogs)
class CustomTestDialog : public wxDialog
{
public:
CustomTestDialog(wxWindow* parent);
wxString GetValue() const { return m_textCtrl->GetValue(); }
private:
wxTextCtrl* m_textCtrl;
};
enum {
ID_INFO_DIALOG = wxID_HIGHEST + 1,
ID_YESNO_DIALOG,
ID_ERROR_DIALOG,
ID_CUSTOM_DIALOG,
ID_INPUT_DIALOG
};
wxBEGIN_EVENT_TABLE(DialogTestFrame, wxFrame)
EVT_BUTTON(ID_INFO_DIALOG, DialogTestFrame::OnInfoDialog)
EVT_BUTTON(ID_YESNO_DIALOG, DialogTestFrame::OnYesNoDialog)
EVT_BUTTON(ID_ERROR_DIALOG, DialogTestFrame::OnErrorDialog)
EVT_BUTTON(ID_CUSTOM_DIALOG, DialogTestFrame::OnCustomDialog)
EVT_BUTTON(ID_INPUT_DIALOG, DialogTestFrame::OnInputDialog)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(DialogTestApp);
bool DialogTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
DialogTestFrame* frame = new DialogTestFrame();
frame->Show(true);
return true;
}
DialogTestFrame::DialogTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxDialog/wxMessageBox WASM Test",
wxDefaultPosition, wxSize(600, 500))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxDialog and wxMessageBox Test\n\n"
"KiCad uses dialogs for alerts, confirmations, and custom property dialogs.\n"
"Click buttons to test different dialog types.");
mainSizer->Add(desc, 0, wxALL, 10);
// wxMessageBox section
wxStaticBoxSizer* msgBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, "wxMessageBox");
wxBoxSizer* msgBtnSizer = new wxBoxSizer(wxHORIZONTAL);
msgBtnSizer->Add(new wxButton(this, ID_INFO_DIALOG, "Info Dialog"), 0, wxALL, 5);
msgBtnSizer->Add(new wxButton(this, ID_YESNO_DIALOG, "Yes/No Dialog"), 0, wxALL, 5);
msgBtnSizer->Add(new wxButton(this, ID_ERROR_DIALOG, "Error Dialog"), 0, wxALL, 5);
msgBoxSizer->Add(msgBtnSizer, 0, wxALIGN_CENTER);
mainSizer->Add(msgBoxSizer, 0, wxEXPAND | wxALL, 10);
// wxDialog section
wxStaticBoxSizer* dlgSizer = new wxStaticBoxSizer(wxVERTICAL, this, "wxDialog");
wxBoxSizer* dlgBtnSizer = new wxBoxSizer(wxHORIZONTAL);
dlgBtnSizer->Add(new wxButton(this, ID_CUSTOM_DIALOG, "Custom Dialog"), 0, wxALL, 5);
dlgBtnSizer->Add(new wxButton(this, ID_INPUT_DIALOG, "Input Dialog"), 0, wxALL, 5);
dlgSizer->Add(dlgBtnSizer, 0, wxALIGN_CENTER);
mainSizer->Add(dlgSizer, 0, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
LogEvent("Dialog test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DIALOG_TEST] wxDialog test app started successfully');
});
#endif
}
void DialogTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DIALOG_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void DialogTestFrame::OnInfoDialog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Info dialog...");
int result = wxMessageBox("This is an informational message.\n\nKiCad uses these for status updates.",
"Information", wxOK | wxICON_INFORMATION, this);
LogEvent(wxString::Format("Info dialog closed with result: %d", result));
}
void DialogTestFrame::OnYesNoDialog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Yes/No dialog...");
int result = wxMessageBox("Do you want to save changes?\n\nKiCad uses these for confirmations.",
"Confirm", wxYES_NO | wxCANCEL | wxICON_QUESTION, this);
wxString resultStr;
switch (result) {
case wxYES: resultStr = "YES"; break;
case wxNO: resultStr = "NO"; break;
case wxCANCEL: resultStr = "CANCEL"; break;
default: resultStr = wxString::Format("Unknown (%d)", result);
}
LogEvent(wxString::Format("Yes/No dialog closed with: %s", resultStr));
}
void DialogTestFrame::OnErrorDialog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Error dialog...");
int result = wxMessageBox("An error has occurred!\n\nKiCad uses these for error messages.",
"Error", wxOK | wxICON_ERROR, this);
LogEvent(wxString::Format("Error dialog closed with result: %d", result));
}
void DialogTestFrame::OnCustomDialog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Custom dialog...");
CustomTestDialog dlg(this);
int result = dlg.ShowModal();
if (result == wxID_OK) {
LogEvent(wxString::Format("Custom dialog OK - value: '%s'", dlg.GetValue()));
} else {
LogEvent("Custom dialog cancelled");
}
}
void DialogTestFrame::OnInputDialog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Input dialog...");
wxTextEntryDialog dlg(this, "Enter a component reference:",
"Input Dialog", "R1");
if (dlg.ShowModal() == wxID_OK) {
LogEvent(wxString::Format("Input dialog OK - value: '%s'", dlg.GetValue()));
} else {
LogEvent("Input dialog cancelled");
}
}
// Custom dialog implementation
CustomTestDialog::CustomTestDialog(wxWindow* parent)
: wxDialog(parent, wxID_ANY, "Custom Test Dialog",
wxDefaultPosition, wxSize(300, 200))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* label = new wxStaticText(this, wxID_ANY,
"This is a custom wxDialog\nlike KiCad's property dialogs:");
mainSizer->Add(label, 0, wxALL, 10);
m_textCtrl = new wxTextCtrl(this, wxID_ANY, "Sample value");
mainSizer->Add(m_textCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 10);
SetSizer(mainSizer);
}

View file

@ -0,0 +1,232 @@
// wxDragDrop Test - Tests HTML5 file drop in WASM
// KiCad uses drag and drop for loading projects, schematics, PCBs, etc.
//
// Tests:
// - External file drops via HTML5 drag and drop API
// - wxDropFilesEvent generation
// - Multiple file drops
// - Visual drop zone feedback
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/dnd.h"
#include "wx/listbox.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
// Forward declarations
class DndTestApp;
class DndTestFrame;
// ============================================================
// DndTestApp
// ============================================================
class DndTestApp : public wxApp
{
public:
bool OnInit() override;
};
// ============================================================
// DndTestFrame - Main test window
// ============================================================
class DndTestFrame : public wxFrame
{
public:
DndTestFrame();
private:
wxPanel* m_dropZone;
wxTextCtrl* m_log;
wxListBox* m_fileList;
bool m_dragOver;
void LogEvent(const wxString& msg);
void OnDropFiles(wxDropFilesEvent& evt);
void OnDropZonePaint(wxPaintEvent& evt);
void OnClearFiles(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
// IDs
enum {
ID_DROP_ZONE = wxID_HIGHEST + 1,
ID_CLEAR_FILES
};
wxBEGIN_EVENT_TABLE(DndTestFrame, wxFrame)
EVT_DROP_FILES(DndTestFrame::OnDropFiles)
EVT_BUTTON(ID_CLEAR_FILES, DndTestFrame::OnClearFiles)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(DndTestApp);
// ============================================================
// App Implementation
// ============================================================
bool DndTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
DndTestFrame* frame = new DndTestFrame();
frame->Show(true);
return true;
}
// ============================================================
// Frame Implementation
// ============================================================
DndTestFrame::DndTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxDragDrop WASM Test",
wxDefaultPosition, wxSize(800, 600)),
m_dragOver(false)
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxDragDrop Test\n\n"
"KiCad uses drag and drop for loading projects, schematics, and PCBs.\n"
"Test by dragging files from your file manager onto the drop zone below.");
mainSizer->Add(desc, 0, wxALL, 10);
// Accepted file types info
wxStaticText* fileTypes = new wxStaticText(this, wxID_ANY,
"Accepted file types: .kicad_pcb, .kicad_sch, .kicad_pro, .dxf, .svg, .png, .jpg, .txt, *.*");
fileTypes->SetForegroundColour(*wxBLUE);
mainSizer->Add(fileTypes, 0, wxLEFT | wxRIGHT, 10);
// Drop zone panel
wxStaticBoxSizer* dropBox = new wxStaticBoxSizer(wxVERTICAL, this, "Drop Zone");
m_dropZone = new wxPanel(this, ID_DROP_ZONE, wxDefaultPosition, wxSize(-1, 150));
m_dropZone->SetBackgroundColour(wxColour(240, 240, 240));
m_dropZone->Bind(wxEVT_PAINT, &DndTestFrame::OnDropZonePaint, this);
dropBox->Add(m_dropZone, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(dropBox, 0, wxEXPAND | wxALL, 10);
// Enable drop target on the frame (wxWidgets will handle EVT_DROP_FILES)
DragAcceptFiles(true);
// File list
wxStaticBoxSizer* fileBox = new wxStaticBoxSizer(wxVERTICAL, this, "Dropped Files");
m_fileList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100));
fileBox->Add(m_fileList, 1, wxEXPAND | wxALL, 5);
wxButton* clearBtn = new wxButton(this, ID_CLEAR_FILES, "Clear Files");
fileBox->Add(clearBtn, 0, wxALIGN_RIGHT | wxALL, 5);
mainSizer->Add(fileBox, 0, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready - Drag files here");
LogEvent("DragDrop test app started");
LogEvent("DragAcceptFiles enabled on frame");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DND_TEST] wxDragDrop test app started successfully');
});
#endif
}
void DndTestFrame::LogEvent(const wxString& msg)
{
if (m_log)
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DND_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void DndTestFrame::OnDropZonePaint(wxPaintEvent& WXUNUSED(evt))
{
wxPaintDC dc(m_dropZone);
// Draw background based on drag state
if (m_dragOver) {
dc.SetBrush(wxBrush(wxColour(200, 230, 255)));
dc.SetPen(wxPen(wxColour(0, 120, 200), 2, wxPENSTYLE_DOT));
} else {
dc.SetBrush(wxBrush(wxColour(240, 240, 240)));
dc.SetPen(wxPen(wxColour(180, 180, 180), 2, wxPENSTYLE_DOT));
}
wxSize sz = m_dropZone->GetClientSize();
dc.DrawRectangle(0, 0, sz.GetWidth(), sz.GetHeight());
// Draw text
dc.SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc.SetTextForeground(m_dragOver ? wxColour(0, 100, 180) : wxColour(100, 100, 100));
wxString text = m_dragOver ? "Release to drop files" : "Drag files here";
wxSize textSize = dc.GetTextExtent(text);
dc.DrawText(text, (sz.GetWidth() - textSize.GetWidth()) / 2,
(sz.GetHeight() - textSize.GetHeight()) / 2);
}
void DndTestFrame::OnDropFiles(wxDropFilesEvent& evt)
{
LogEvent("=== wxDropFilesEvent received! ===");
int numFiles = evt.GetNumberOfFiles();
wxString* files = evt.GetFiles();
LogEvent(wxString::Format("Number of files: %d", numFiles));
for (int i = 0; i < numFiles; i++) {
wxString filePath = files[i];
LogEvent(wxString::Format("File %d: %s", i + 1, filePath));
// Add to file list
m_fileList->Append(filePath);
// Try to read file info
if (wxFileExists(filePath)) {
wxFile file(filePath);
if (file.IsOpened()) {
wxFileOffset size = file.Length();
LogEvent(wxString::Format(" Size: %lld bytes", (long long)size));
file.Close();
}
} else {
LogEvent(wxString::Format(" (File not found in WASM filesystem)"));
}
}
LogEvent("=== Drop complete ===");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[DND_EVENT] Drop complete: ' + $0 + ' files');
}, numFiles);
#endif
}
void DndTestFrame::OnClearFiles(wxCommandEvent& WXUNUSED(evt))
{
m_fileList->Clear();
LogEvent("File list cleared");
}

View file

@ -0,0 +1,154 @@
// Early Size Test - Reproduces KiCad's pattern of calling GetClientSize() before Show()
// This tests whether wxWidgets WASM port returns correct sizes during frame construction.
//
// KiCad's EDA_BASE_FRAME::commonInit() does:
// m_frameSize = defaultSize(); // 1280x720
// GetClientSize(&m_frameSize.x, &m_frameSize.y); // Overwrites with actual client size
//
// In WASM, GetClientSize() returns 20x20 if called before the frame is shown/sized.
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/display.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class EarlySizeTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class EarlySizeTestFrame : public wxFrame
{
public:
EarlySizeTestFrame();
private:
wxSize m_earlyClientSize; // Captured before Show()
wxSize m_earlyFrameSize;
wxStaticText* m_resultLabel;
void OnPaint(wxPaintEvent& evt);
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(EarlySizeTestFrame, wxFrame)
EVT_PAINT(EarlySizeTestFrame::OnPaint)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(EarlySizeTestApp);
bool EarlySizeTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
// Create frame with wxDefaultSize (like KiCad does)
EarlySizeTestFrame* frame = new EarlySizeTestFrame();
// Show the frame (this is where sizing should happen)
frame->Show(true);
return true;
}
EarlySizeTestFrame::EarlySizeTestFrame()
: wxFrame(nullptr, wxID_ANY, "Early Size Test",
wxDefaultPosition, wxDefaultSize) // No explicit size!
{
// === THIS IS THE KEY TEST ===
// KiCad calls GetClientSize() in commonInit(), BEFORE Show()
// In WASM, this should NOT return 20x20
GetClientSize(&m_earlyClientSize.x, &m_earlyClientSize.y);
m_earlyFrameSize = GetSize();
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[EARLYSIZE_TEST] Constructor called - BEFORE Show()');
console.log('[EARLYSIZE_TEST] Early client size: ' + $0 + 'x' + $1);
console.log('[EARLYSIZE_TEST] Early frame size: ' + $2 + 'x' + $3);
}, m_earlyClientSize.x, m_earlyClientSize.y,
m_earlyFrameSize.x, m_earlyFrameSize.y);
#endif
// Create UI
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"Early Size Test\n\n"
"This test reproduces KiCad's pattern:\n"
"- Frame created with wxDefaultSize\n"
"- GetClientSize() called in constructor BEFORE Show()\n"
"- Size should NOT be 20x20!");
mainSizer->Add(desc, 0, wxALL, 10);
// Results
wxStaticBoxSizer* resultBox = new wxStaticBoxSizer(wxVERTICAL, this, "Results (from constructor)");
wxString resultText = wxString::Format(
"Early Client Size: %dx%d\nEarly Frame Size: %dx%d",
m_earlyClientSize.x, m_earlyClientSize.y,
m_earlyFrameSize.x, m_earlyFrameSize.y);
m_resultLabel = new wxStaticText(this, wxID_ANY, resultText);
resultBox->Add(m_resultLabel, 0, wxALL, 5);
mainSizer->Add(resultBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
// Maximize like KiCad does
Maximize(true);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[EARLYSIZE_TEST] Early size test app started');
});
#endif
}
void EarlySizeTestFrame::OnPaint(wxPaintEvent& evt)
{
evt.Skip();
// Log final result after first paint
static bool logged = false;
if (!logged) {
logged = true;
wxSize currentClient;
GetClientSize(&currentClient.x, &currentClient.y);
wxSize currentFrame = GetSize();
#ifdef __EMSCRIPTEN__
// The key assertion: early client size should be > 100
// If it's 20x20, that's the bug!
bool earlyClientOk = (m_earlyClientSize.x > 100 && m_earlyClientSize.y > 100);
bool earlyFrameOk = (m_earlyFrameSize.x > 100 && m_earlyFrameSize.y > 100);
EM_ASM({
console.log('[EARLYSIZE_TEST] After Show() - current client: ' + $0 + 'x' + $1);
console.log('[EARLYSIZE_TEST] After Show() - current frame: ' + $2 + 'x' + $3);
if ($4 && $5) {
console.log('[EARLYSIZE_TEST] PASS: Early sizes were reasonable');
} else {
console.error('[EARLYSIZE_TEST] FAIL: Early sizes were tiny!');
console.error('[EARLYSIZE_TEST] Early client was: ' + $6 + 'x' + $7);
console.error('[EARLYSIZE_TEST] Early frame was: ' + $8 + 'x' + $9);
}
}, currentClient.x, currentClient.y,
currentFrame.x, currentFrame.y,
earlyClientOk ? 1 : 0, earlyFrameOk ? 1 : 0,
m_earlyClientSize.x, m_earlyClientSize.y,
m_earlyFrameSize.x, m_earlyFrameSize.y);
#endif
}
}

View file

@ -0,0 +1,162 @@
// wxFileDialog Test - Tests file dialog functionality in WASM
// KiCad uses file dialogs for opening/saving schematics, PCBs, footprints
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/filedlg.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class FileDialogTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class FileDialogTestFrame : public wxFrame
{
public:
FileDialogTestFrame();
private:
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnOpenFile(wxCommandEvent& evt);
void OnSaveFile(wxCommandEvent& evt);
void OnOpenMultiple(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_OPEN_FILE = wxID_HIGHEST + 1,
ID_SAVE_FILE,
ID_OPEN_MULTIPLE
};
wxBEGIN_EVENT_TABLE(FileDialogTestFrame, wxFrame)
EVT_BUTTON(ID_OPEN_FILE, FileDialogTestFrame::OnOpenFile)
EVT_BUTTON(ID_SAVE_FILE, FileDialogTestFrame::OnSaveFile)
EVT_BUTTON(ID_OPEN_MULTIPLE, FileDialogTestFrame::OnOpenMultiple)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(FileDialogTestApp);
bool FileDialogTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
FileDialogTestFrame* frame = new FileDialogTestFrame();
frame->Show(true);
return true;
}
FileDialogTestFrame::FileDialogTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxFileDialog WASM Test",
wxDefaultPosition, wxSize(600, 400))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxFileDialog Test\n\n"
"Tests file dialog operations that KiCad uses for open/save.\n"
"Note: Browser file access is typically restricted.");
mainSizer->Add(desc, 0, wxALL, 10);
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
buttonSizer->Add(new wxButton(this, ID_OPEN_FILE, "Open File..."), 0, wxALL, 5);
buttonSizer->Add(new wxButton(this, ID_SAVE_FILE, "Save File..."), 0, wxALL, 5);
buttonSizer->Add(new wxButton(this, ID_OPEN_MULTIPLE, "Open Multiple..."), 0, wxALL, 5);
mainSizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxALL, 10);
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
LogEvent("FileDialog test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[FILEDIALOG_TEST] wxFileDialog test app started successfully');
});
#endif
}
void FileDialogTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[FILEDIALOG_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void FileDialogTestFrame::OnOpenFile(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening file dialog...");
wxFileDialog openDialog(this, "Open File", "", "",
"All files (*.*)|*.*|KiCad files (*.kicad_*)|*.kicad_*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openDialog.ShowModal() == wxID_OK) {
wxString path = openDialog.GetPath();
LogEvent(wxString::Format("Selected file: %s", path));
} else {
LogEvent("Open dialog cancelled");
}
}
void FileDialogTestFrame::OnSaveFile(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening save dialog...");
wxFileDialog saveDialog(this, "Save File", "", "untitled.txt",
"Text files (*.txt)|*.txt|All files (*.*)|*.*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (saveDialog.ShowModal() == wxID_OK) {
wxString path = saveDialog.GetPath();
LogEvent(wxString::Format("Save to: %s", path));
} else {
LogEvent("Save dialog cancelled");
}
}
void FileDialogTestFrame::OnOpenMultiple(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening multiple file dialog...");
wxFileDialog openDialog(this, "Open Multiple Files", "", "",
"All files (*.*)|*.*",
wxFD_OPEN | wxFD_MULTIPLE);
if (openDialog.ShowModal() == wxID_OK) {
wxArrayString paths;
openDialog.GetPaths(paths);
LogEvent(wxString::Format("Selected %zu files:", paths.GetCount()));
for (size_t i = 0; i < paths.GetCount(); i++) {
LogEvent(wxString::Format(" %s", paths[i]));
}
} else {
LogEvent("Multiple file dialog cancelled");
}
}

View file

@ -0,0 +1,250 @@
// wxFontEnumerator Test - Tests Local Font Access API integration
// Tests: wxFontEnumerator::EnumerateFacenames(), font listing, sample rendering
#include "wx/wx.h"
#include "wx/fontenum.h"
#include "wx/listbox.h"
// Custom font enumerator that collects font names
class FontCollector : public wxFontEnumerator
{
public:
wxArrayString& GetFonts() { return m_fonts; }
protected:
virtual bool OnFacename(const wxString& facename) override
{
m_fonts.Add(facename);
return true; // Continue enumeration
}
private:
wxArrayString m_fonts;
};
class FontEnumFrame : public wxFrame
{
public:
FontEnumFrame() : wxFrame(nullptr, wxID_ANY, "wxFontEnumerator Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
// Left panel - font list
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* listLabel = new wxStaticText(mainPanel, wxID_ANY,
"System Fonts (via Local Font Access API):");
leftSizer->Add(listLabel, 0, wxALL, 5);
m_fontList = new wxListBox(mainPanel, wxID_ANY, wxDefaultPosition,
wxSize(300, -1));
m_fontList->Bind(wxEVT_LISTBOX, &FontEnumFrame::OnFontSelected, this);
leftSizer->Add(m_fontList, 1, wxEXPAND | wxALL, 5);
m_enumButton = new wxButton(mainPanel, wxID_ANY, "Enumerate Fonts");
m_enumButton->Bind(wxEVT_BUTTON, &FontEnumFrame::OnEnumerateFonts, this);
leftSizer->Add(m_enumButton, 0, wxALL, 5);
m_statusText = new wxStaticText(mainPanel, wxID_ANY, "Click 'Enumerate Fonts' to start");
leftSizer->Add(m_statusText, 0, wxALL, 5);
mainSizer->Add(leftSizer, 0, wxEXPAND | wxALL, 5);
// Right panel - font preview
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* previewLabel = new wxStaticText(mainPanel, wxID_ANY, "Font Preview:");
rightSizer->Add(previewLabel, 0, wxALL, 5);
m_previewPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
wxSize(-1, 200));
m_previewPanel->SetBackgroundColour(*wxWHITE);
m_previewPanel->Bind(wxEVT_PAINT, &FontEnumFrame::OnPaintPreview, this);
rightSizer->Add(m_previewPanel, 0, wxEXPAND | wxALL, 5);
// Sample text sizes
wxStaticText* sizesLabel = new wxStaticText(mainPanel, wxID_ANY, "Sample Text at Different Sizes:");
rightSizer->Add(sizesLabel, 0, wxALL, 5);
m_samplesPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
wxSize(-1, 250));
m_samplesPanel->SetBackgroundColour(*wxWHITE);
m_samplesPanel->Bind(wxEVT_PAINT, &FontEnumFrame::OnPaintSamples, this);
rightSizer->Add(m_samplesPanel, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(rightSizer, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Event log
m_log = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
wxBoxSizer* frameSizer = new wxBoxSizer(wxVERTICAL);
frameSizer->Add(mainPanel, 1, wxEXPAND);
frameSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
SetSizer(frameSizer);
CreateStatusBar();
SetStatusText("Font Enumeration Test - Uses Local Font Access API");
Log("Font enumeration test app started");
Log("Note: Requires Chrome/Edge 103+ and user permission");
// Auto-enumerate fonts on startup for testing
CallAfter([this]() {
wxCommandEvent evt;
OnEnumerateFonts(evt);
});
}
private:
void OnEnumerateFonts(wxCommandEvent& event)
{
m_fontList->Clear();
m_selectedFont.Clear();
m_previewPanel->Refresh();
m_samplesPanel->Refresh();
Log("Starting font enumeration...");
m_statusText->SetLabel("Enumerating fonts...");
FontCollector collector;
bool success = collector.EnumerateFacenames();
if (success)
{
wxArrayString& fonts = collector.GetFonts();
int count = fonts.GetCount();
Log(wxString::Format("Found %d font families", count));
m_statusText->SetLabel(wxString::Format("Found %d fonts", count));
for (size_t i = 0; i < fonts.GetCount(); i++)
{
m_fontList->Append(fonts[i]);
}
if (count > 0)
{
m_fontList->SetSelection(0);
m_selectedFont = fonts[0];
m_previewPanel->Refresh();
m_samplesPanel->Refresh();
}
}
else
{
Log("Font enumeration failed or permission denied");
m_statusText->SetLabel("Enumeration failed - check console");
// Add a note about the API requirement
m_fontList->Append("(Font enumeration unavailable)");
m_fontList->Append("Requires:");
m_fontList->Append("- Chrome/Edge 103+");
m_fontList->Append("- User permission");
}
}
void OnFontSelected(wxCommandEvent& event)
{
int sel = m_fontList->GetSelection();
if (sel != wxNOT_FOUND)
{
m_selectedFont = m_fontList->GetString(sel);
Log(wxString::Format("Selected font: %s", m_selectedFont));
m_previewPanel->Refresh();
m_samplesPanel->Refresh();
}
}
void OnPaintPreview(wxPaintEvent& event)
{
wxPaintDC dc(m_previewPanel);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
if (m_selectedFont.IsEmpty())
{
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
dc.SetTextForeground(wxColour(128, 128, 128));
dc.DrawText("Select a font to preview", 10, 10);
return;
}
// Draw font name and sample text
wxFont font(24, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
false, m_selectedFont);
dc.SetFont(font);
dc.SetTextForeground(*wxBLACK);
int y = 10;
dc.DrawText(m_selectedFont, 10, y);
y += 40;
dc.DrawText("The quick brown fox jumps over the lazy dog", 10, y);
y += 40;
dc.DrawText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10, y);
y += 40;
dc.DrawText("abcdefghijklmnopqrstuvwxyz", 10, y);
y += 40;
dc.DrawText("0123456789 !@#$%^&*()", 10, y);
}
void OnPaintSamples(wxPaintEvent& event)
{
wxPaintDC dc(m_samplesPanel);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
if (m_selectedFont.IsEmpty())
{
return;
}
int sizes[] = {8, 10, 12, 14, 16, 18, 24, 32};
int y = 10;
for (int size : sizes)
{
wxFont font(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
false, m_selectedFont);
dc.SetFont(font);
dc.SetTextForeground(*wxBLACK);
wxString sample = wxString::Format("%dpt: Sample Text AaBbCc 123", size);
dc.DrawText(sample, 10, y);
y += size + 8;
}
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxListBox* m_fontList;
wxButton* m_enumButton;
wxStaticText* m_statusText;
wxPanel* m_previewPanel;
wxPanel* m_samplesPanel;
wxTextCtrl* m_log;
wxString m_selectedFont;
};
class FontEnumApp : public wxApp
{
public:
virtual bool OnInit() override
{
FontEnumFrame* frame = new FontEnumFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(FontEnumApp);

View file

@ -0,0 +1,219 @@
// Minimal wxGrid Test - Tests if wxGrid works in WASM
// This is a SEPARATE test app because wxGrid may crash the app at startup
// If this page loads successfully, wxGrid is working!
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/grid.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class GridTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class GridTestFrame : public wxFrame
{
public:
GridTestFrame();
private:
wxGrid* m_grid;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
// Grid event handlers
void OnGridCellSelect(wxGridEvent& evt);
void OnGridCellChange(wxGridEvent& evt);
void OnGridLabelClick(wxGridEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_GRID = wxID_HIGHEST + 1,
ID_LOG
};
wxBEGIN_EVENT_TABLE(GridTestFrame, wxFrame)
EVT_GRID_SELECT_CELL(GridTestFrame::OnGridCellSelect)
EVT_GRID_CELL_CHANGED(GridTestFrame::OnGridCellChange)
EVT_GRID_LABEL_LEFT_CLICK(GridTestFrame::OnGridLabelClick)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(GridTestApp);
bool GridTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
GridTestFrame* frame = new GridTestFrame();
frame->Show(true);
return true;
}
GridTestFrame::GridTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxGrid WASM Test",
wxDefaultPosition, wxSize(600, 500))
{
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 1. GridTestFrame constructor start'); });
#endif
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 2. BoxSizer created'); });
#endif
// Status message
wxStaticText* status = new wxStaticText(this, wxID_ANY,
"SUCCESS: wxGrid initialized! If you see this, wxGrid is working in WASM.");
status->SetForegroundColour(*wxGREEN);
mainSizer->Add(status, 0, wxALL, 10);
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 3. StaticText created'); });
#endif
// Create wxGrid - THIS IS THE CRITICAL TEST
// If wxGrid doesn't work in WASM, the app will crash HERE
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 4. About to create wxGrid...'); });
#endif
m_grid = new wxGrid(this, ID_GRID, wxDefaultPosition, wxSize(500, 200));
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 5. wxGrid created successfully!'); });
#endif
// Setup grid with sample data (like KiCad's property grids)
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 6. About to call CreateGrid(5, 4)...'); });
#endif
m_grid->CreateGrid(5, 4);
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[GRID_DEBUG] 7. CreateGrid completed!'); });
#endif
// Set column labels (similar to KiCad's property dialogs)
m_grid->SetColLabelValue(0, "Property");
m_grid->SetColLabelValue(1, "Value");
m_grid->SetColLabelValue(2, "Units");
m_grid->SetColLabelValue(3, "Description");
// Set column widths
m_grid->SetColSize(0, 100);
m_grid->SetColSize(1, 80);
m_grid->SetColSize(2, 50);
m_grid->SetColSize(3, 150);
// Fill with sample data (like KiCad track/via properties)
m_grid->SetCellValue(0, 0, "Track Width");
m_grid->SetCellValue(0, 1, "0.25");
m_grid->SetCellValue(0, 2, "mm");
m_grid->SetCellValue(0, 3, "Default track width");
m_grid->SetCellValue(1, 0, "Via Size");
m_grid->SetCellValue(1, 1, "0.80");
m_grid->SetCellValue(1, 2, "mm");
m_grid->SetCellValue(1, 3, "Via outer diameter");
m_grid->SetCellValue(2, 0, "Via Drill");
m_grid->SetCellValue(2, 1, "0.40");
m_grid->SetCellValue(2, 2, "mm");
m_grid->SetCellValue(2, 3, "Via drill diameter");
m_grid->SetCellValue(3, 0, "Clearance");
m_grid->SetCellValue(3, 1, "0.20");
m_grid->SetCellValue(3, 2, "mm");
m_grid->SetCellValue(3, 3, "Min clearance");
m_grid->SetCellValue(4, 0, "Net Class");
m_grid->SetCellValue(4, 1, "Default");
m_grid->SetCellValue(4, 2, "-");
m_grid->SetCellValue(4, 3, "Net class name");
// Make first column read-only (like property names in KiCad)
for (int row = 0; row < 5; row++) {
m_grid->SetReadOnly(row, 0);
m_grid->SetReadOnly(row, 2);
m_grid->SetReadOnly(row, 3);
}
mainSizer->Add(m_grid, 1, wxEXPAND | wxALL, 10);
// Event log
wxStaticBox* logBox = new wxStaticBox(this, wxID_ANY, "Event Log");
wxStaticBoxSizer* logSizer = new wxStaticBoxSizer(logBox, wxVERTICAL);
m_log = new wxTextCtrl(this, ID_LOG, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
logSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logSizer, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
LogEvent("wxGrid initialized successfully!");
LogEvent("Try clicking cells, editing values, etc.");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[GRID_TEST] wxGrid test app started successfully!');
console.log('[GRID_TEST] If you see this message, wxGrid is working in WASM!');
});
#endif
}
void GridTestFrame::LogEvent(const wxString& msg)
{
// Guard against events firing before m_log is initialized
if (!m_log)
return;
m_log->AppendText(msg + "\n");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[GRID_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void GridTestFrame::OnGridCellSelect(wxGridEvent& evt)
{
LogEvent(wxString::Format("Cell selected: row=%d, col=%d, value='%s'",
evt.GetRow(), evt.GetCol(),
m_grid->GetCellValue(evt.GetRow(), evt.GetCol())));
evt.Skip();
}
void GridTestFrame::OnGridCellChange(wxGridEvent& evt)
{
LogEvent(wxString::Format("Cell changed: row=%d, col=%d, new value='%s'",
evt.GetRow(), evt.GetCol(),
m_grid->GetCellValue(evt.GetRow(), evt.GetCol())));
evt.Skip();
}
void GridTestFrame::OnGridLabelClick(wxGridEvent& evt)
{
if (evt.GetRow() >= 0) {
LogEvent(wxString::Format("Row label clicked: row=%d", evt.GetRow()));
} else if (evt.GetCol() >= 0) {
LogEvent(wxString::Format("Column label clicked: col=%d ('%s')",
evt.GetCol(), m_grid->GetColLabelValue(evt.GetCol())));
}
evt.Skip();
}

View file

@ -0,0 +1,206 @@
// wxGrid Cell Editing Test - Property editing simulation
// Tests wxGrid with editable cells for KiCad property panels
#include "wx/wx.h"
#include "wx/grid.h"
class GridEditFrame : public wxFrame
{
public:
GridEditFrame() : wxFrame(nullptr, wxID_ANY, "wxGrid Cell Editing Test",
wxDefaultPosition, wxSize(900, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxGrid for editable property tables.\n"
"Tests cell editing with different editor types: text, number, choice, checkbox.");
mainSizer->Add(desc, 0, wxALL, 5);
// Controls
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* btnAdd = new wxButton(mainPanel, wxID_ANY, "Add Row");
wxButton* btnDelete = new wxButton(mainPanel, wxID_ANY, "Delete Row");
wxButton* btnClear = new wxButton(mainPanel, wxID_ANY, "Clear Selection");
btnAdd->Bind(wxEVT_BUTTON, &GridEditFrame::OnAddRow, this);
btnDelete->Bind(wxEVT_BUTTON, &GridEditFrame::OnDeleteRow, this);
btnClear->Bind(wxEVT_BUTTON, &GridEditFrame::OnClearSelection, this);
controlSizer->Add(btnAdd, 0, wxRIGHT, 5);
controlSizer->Add(btnDelete, 0, wxRIGHT, 5);
controlSizer->Add(btnClear, 0);
mainSizer->Add(controlSizer, 0, wxALL, 5);
// Create grid
m_grid = new wxGrid(mainPanel, wxID_ANY);
m_grid->CreateGrid(10, 5);
// Set column headers
m_grid->SetColLabelValue(0, "Reference");
m_grid->SetColLabelValue(1, "Value");
m_grid->SetColLabelValue(2, "Footprint");
m_grid->SetColLabelValue(3, "Quantity");
m_grid->SetColLabelValue(4, "DNP");
// Set column widths
m_grid->SetColSize(0, 100);
m_grid->SetColSize(1, 120);
m_grid->SetColSize(2, 200);
m_grid->SetColSize(3, 80);
m_grid->SetColSize(4, 60);
// Set up cell editors
// Column 0-2: Text editors (default)
// Column 3: Number editor
m_grid->SetColFormatNumber(3);
// Column 4: Boolean editor (checkbox)
m_grid->SetColFormatBool(4);
// Populate with KiCad-like component data
PopulateGrid();
// Set up choice editor for footprint column
wxString footprints[] = {
"Resistor_SMD:R_0402", "Resistor_SMD:R_0603", "Resistor_SMD:R_0805",
"Capacitor_SMD:C_0402", "Capacitor_SMD:C_0603", "Capacitor_SMD:C_0805",
"Package_QFP:LQFP-48", "Package_QFP:LQFP-64", "Package_QFP:LQFP-100"
};
wxGridCellChoiceEditor* choiceEditor = new wxGridCellChoiceEditor(9, footprints);
m_grid->SetColAttr(2, new wxGridCellAttr());
m_grid->GetOrCreateCellAttr(0, 2)->SetEditor(choiceEditor);
mainSizer->Add(m_grid, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Bind grid events
m_grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridEditFrame::OnCellChanged, this);
m_grid->Bind(wxEVT_GRID_SELECT_CELL, &GridEditFrame::OnCellSelected, this);
m_grid->Bind(wxEVT_GRID_EDITOR_SHOWN, &GridEditFrame::OnEditorShown, this);
m_grid->Bind(wxEVT_GRID_EDITOR_HIDDEN, &GridEditFrame::OnEditorHidden, this);
// Status bar
CreateStatusBar();
SetStatusText("Grid editing test app started");
Log("Grid editing test app started");
Log("Double-click cells to edit. Use choice dropdown for Footprint column.");
}
private:
void PopulateGrid()
{
// Reference, Value, Footprint, Quantity, DNP
SetRow(0, "R1", "10k", "Resistor_SMD:R_0402", "1", "0");
SetRow(1, "R2", "4.7k", "Resistor_SMD:R_0402", "1", "0");
SetRow(2, "R3", "100", "Resistor_SMD:R_0603", "2", "0");
SetRow(3, "C1", "100nF", "Capacitor_SMD:C_0402", "1", "0");
SetRow(4, "C2", "10uF", "Capacitor_SMD:C_0805", "1", "0");
SetRow(5, "U1", "STM32F103", "Package_QFP:LQFP-48", "1", "0");
SetRow(6, "U2", "74HC595", "Package_QFP:LQFP-64", "1", "1");
SetRow(7, "J1", "USB-C", "Connector_USB:USB_C", "1", "0");
SetRow(8, "D1", "LED_Red", "LED_SMD:LED_0603", "1", "0");
SetRow(9, "Q1", "2N7002", "Package_TO:SOT-23", "1", "0");
}
void SetRow(int row, const wxString& ref, const wxString& val,
const wxString& fp, const wxString& qty, const wxString& dnp)
{
m_grid->SetCellValue(row, 0, ref);
m_grid->SetCellValue(row, 1, val);
m_grid->SetCellValue(row, 2, fp);
m_grid->SetCellValue(row, 3, qty);
m_grid->SetCellValue(row, 4, dnp);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void OnAddRow(wxCommandEvent& event)
{
int newRow = m_grid->GetNumberRows();
m_grid->AppendRows(1);
m_grid->SetCellValue(newRow, 0, wxString::Format("NEW%d", newRow + 1));
m_grid->SetCellValue(newRow, 3, "1");
m_grid->SetCellValue(newRow, 4, "0");
Log(wxString::Format("Added row %d", newRow));
}
void OnDeleteRow(wxCommandEvent& event)
{
int row = m_grid->GetGridCursorRow();
if (row >= 0 && m_grid->GetNumberRows() > 1)
{
wxString ref = m_grid->GetCellValue(row, 0);
m_grid->DeleteRows(row, 1);
Log(wxString::Format("Deleted row: %s", ref));
}
}
void OnClearSelection(wxCommandEvent& event)
{
m_grid->ClearSelection();
Log("Selection cleared");
}
void OnCellChanged(wxGridEvent& event)
{
int row = event.GetRow();
int col = event.GetCol();
wxString value = m_grid->GetCellValue(row, col);
wxString colName = m_grid->GetColLabelValue(col);
Log(wxString::Format("Cell changed: [%d,%d] %s = '%s'", row, col, colName, value));
}
void OnCellSelected(wxGridEvent& event)
{
int row = event.GetRow();
int col = event.GetCol();
Log(wxString::Format("Cell selected: [%d,%d]", row, col));
event.Skip();
}
void OnEditorShown(wxGridEvent& event)
{
int row = event.GetRow();
int col = event.GetCol();
Log(wxString::Format("Editor opened: [%d,%d]", row, col));
event.Skip();
}
void OnEditorHidden(wxGridEvent& event)
{
Log("Editor closed");
event.Skip();
}
wxGrid* m_grid;
wxTextCtrl* m_log;
};
class GridEditApp : public wxApp
{
public:
virtual bool OnInit() override
{
GridEditFrame* frame = new GridEditFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(GridEditApp);

View file

@ -0,0 +1,330 @@
// wxGrid Custom Cell Renderers Test - KiCad-style grid cells
// Tests custom cell rendering: color cells, icon+text, checkboxes, striped rows
#include "wx/wx.h"
#include "wx/grid.h"
#include "wx/dcmemory.h"
#include "wx/notebook.h"
// Custom Color Cell Renderer - like KiCad's layer color swatches
class ColorCellRenderer : public wxGridCellRenderer
{
public:
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
const wxRect& rect, int row, int col, bool isSelected) override
{
wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
wxString value = grid.GetCellValue(row, col);
wxColour color;
if (color.Set(value))
{
// Draw color swatch
wxRect colorRect = rect;
colorRect.Deflate(4);
dc.SetBrush(wxBrush(color));
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(colorRect);
}
}
virtual wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
int row, int col) override
{
return wxSize(60, 20);
}
virtual wxGridCellRenderer* Clone() const override
{
return new ColorCellRenderer();
}
};
// Custom Icon+Text Renderer - like KiCad's footprint list with icons
class IconTextRenderer : public wxGridCellStringRenderer
{
public:
IconTextRenderer(const wxColour& iconColor = *wxBLUE) : m_iconColor(iconColor) {}
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
const wxRect& rect, int row, int col, bool isSelected) override
{
// Draw background
wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
// Draw icon (small colored square)
wxRect iconRect(rect.x + 2, rect.y + 3, 14, 14);
dc.SetBrush(wxBrush(m_iconColor));
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(iconRect);
// Draw text offset by icon width
wxRect textRect = rect;
textRect.x += 20;
textRect.width -= 20;
dc.SetTextForeground(isSelected ? *wxWHITE : *wxBLACK);
dc.DrawLabel(grid.GetCellValue(row, col), textRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
virtual wxGridCellRenderer* Clone() const override
{
return new IconTextRenderer(m_iconColor);
}
private:
wxColour m_iconColor;
};
// Striped Row Renderer - alternating row colors like KiCad's symbol editor
class StripedRenderer : public wxGridCellStringRenderer
{
public:
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
const wxRect& rect, int row, int col, bool isSelected) override
{
// Alternating background colors
if (!isSelected)
{
wxColour bgColor = (row % 2 == 0) ? wxColour(255, 255, 255) : wxColour(240, 240, 245);
dc.SetBrush(wxBrush(bgColor));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rect);
}
// Draw text
wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
}
virtual wxGridCellRenderer* Clone() const override
{
return new StripedRenderer();
}
};
class GridRenderersFrame : public wxFrame
{
public:
GridRenderersFrame() : wxFrame(nullptr, wxID_ANY, "wxGrid Custom Cell Renderers Test",
wxDefaultPosition, wxSize(1000, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses custom grid cell renderers for color swatches, icons, and striped rows.\n"
"Tests: Color cells, Icon+Text cells, Striped rows, Checkbox cells.");
mainSizer->Add(desc, 0, wxALL, 5);
// Create notebook with different grid examples
m_notebook = new wxNotebook(mainPanel, wxID_ANY);
// Tab 1: Color Cells (like KiCad layer manager)
CreateColorGrid();
// Tab 2: Icon + Text (like footprint browser)
CreateIconTextGrid();
// Tab 3: Striped rows with checkboxes
CreateStripedGrid();
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Grid renderers test app started");
Log("Grid renderers test app started");
}
private:
void CreateColorGrid()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxGrid* grid = new wxGrid(panel, wxID_ANY);
grid->CreateGrid(8, 3);
grid->SetColLabelValue(0, "Layer");
grid->SetColLabelValue(1, "Color");
grid->SetColLabelValue(2, "Visible");
grid->SetColSize(0, 150);
grid->SetColSize(1, 100);
grid->SetColSize(2, 80);
// Set up color renderer for column 1 (read-only display)
wxGridCellAttr* colorAttr = new wxGridCellAttr();
colorAttr->SetRenderer(new ColorCellRenderer());
colorAttr->SetReadOnly(true); // Display only - would need dialog to edit
grid->SetColAttr(1, colorAttr);
// Boolean column
grid->SetColFormatBool(2);
// Layer data
const char* layers[] = {"F.Cu", "B.Cu", "F.SilkS", "B.SilkS", "F.Mask", "B.Mask", "Edge.Cuts", "Dwgs.User"};
const char* colors[] = {"#FF0000", "#0000FF", "#FFFF00", "#FF00FF", "#00FF00", "#00FFFF", "#FFFFFF", "#808080"};
for (int i = 0; i < 8; i++)
{
grid->SetCellValue(i, 0, layers[i]);
grid->SetCellValue(i, 1, colors[i]);
grid->SetCellValue(i, 2, "1");
}
grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridRenderersFrame::OnCellChanged, this);
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Color Cells");
m_colorGrid = grid;
}
void CreateIconTextGrid()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxGrid* grid = new wxGrid(panel, wxID_ANY);
grid->CreateGrid(10, 3);
grid->SetColLabelValue(0, "Component");
grid->SetColLabelValue(1, "Footprint");
grid->SetColLabelValue(2, "Library");
grid->SetColSize(0, 180);
grid->SetColSize(1, 250);
grid->SetColSize(2, 150);
// Set up icon+text renderer
wxGridCellAttr* iconAttr = new wxGridCellAttr();
iconAttr->SetRenderer(new IconTextRenderer(*wxRED));
grid->SetColAttr(0, iconAttr);
wxGridCellAttr* iconAttr2 = new wxGridCellAttr();
iconAttr2->SetRenderer(new IconTextRenderer(wxColour(0, 128, 0)));
grid->SetColAttr(1, iconAttr2);
// Data
const char* components[] = {"R_0402", "C_0603", "LED_0805", "STM32F4", "USB_C",
"MOSFET_SOT23", "LDO_SOT223", "Crystal_3225", "Header_2x5", "Connector_JST"};
const char* footprints[] = {"Resistor_SMD:R_0402", "Capacitor_SMD:C_0603", "LED_SMD:LED_0805",
"Package_QFP:LQFP-100", "Connector_USB:USB_C",
"Package_TO:SOT-23", "Package_TO:SOT-223", "Crystal:Crystal_3225",
"Connector_Pin:2x5", "Connector_JST:JST_PH_4"};
const char* libraries[] = {"Resistor_SMD", "Capacitor_SMD", "LED_SMD", "Package_QFP",
"Connector_USB", "Package_TO", "Package_TO", "Crystal",
"Connector_Pin", "Connector_JST"};
for (int i = 0; i < 10; i++)
{
grid->SetCellValue(i, 0, components[i]);
grid->SetCellValue(i, 1, footprints[i]);
grid->SetCellValue(i, 2, libraries[i]);
}
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Icon+Text");
m_iconGrid = grid;
}
void CreateStripedGrid()
{
wxPanel* panel = new wxPanel(m_notebook);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxGrid* grid = new wxGrid(panel, wxID_ANY);
grid->CreateGrid(12, 4);
grid->SetColLabelValue(0, "Reference");
grid->SetColLabelValue(1, "Value");
grid->SetColLabelValue(2, "DNP");
grid->SetColLabelValue(3, "Excluded");
grid->SetColSize(0, 100);
grid->SetColSize(1, 150);
grid->SetColSize(2, 80);
grid->SetColSize(3, 80);
// Apply striped renderer to text columns
wxGridCellAttr* stripedAttr = new wxGridCellAttr();
stripedAttr->SetRenderer(new StripedRenderer());
grid->SetColAttr(0, stripedAttr);
wxGridCellAttr* stripedAttr2 = new wxGridCellAttr();
stripedAttr2->SetRenderer(new StripedRenderer());
grid->SetColAttr(1, stripedAttr2);
// Boolean columns
grid->SetColFormatBool(2);
grid->SetColFormatBool(3);
// BOM-like data
const char* refs[] = {"R1", "R2", "R3", "R4", "C1", "C2", "C3", "U1", "U2", "J1", "D1", "Q1"};
const char* values[] = {"10k", "4.7k", "100", "1M", "100nF", "10uF", "22pF", "STM32F103",
"74HC595", "USB-C", "LED_Red", "2N7002"};
const char* dnp[] = {"0", "0", "1", "0", "0", "0", "0", "0", "1", "0", "0", "0"};
const char* excluded[] = {"0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0"};
for (int i = 0; i < 12; i++)
{
grid->SetCellValue(i, 0, refs[i]);
grid->SetCellValue(i, 1, values[i]);
grid->SetCellValue(i, 2, dnp[i]);
grid->SetCellValue(i, 3, excluded[i]);
}
grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridRenderersFrame::OnCellChanged, this);
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
panel->SetSizer(sizer);
m_notebook->AddPage(panel, "Striped+Checkboxes");
m_stripedGrid = grid;
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void OnCellChanged(wxGridEvent& event)
{
wxGrid* grid = dynamic_cast<wxGrid*>(event.GetEventObject());
if (grid)
{
int row = event.GetRow();
int col = event.GetCol();
wxString value = grid->GetCellValue(row, col);
Log(wxString::Format("Cell [%d,%d] changed to: %s", row, col, value));
}
}
wxNotebook* m_notebook;
wxGrid* m_colorGrid;
wxGrid* m_iconGrid;
wxGrid* m_stripedGrid;
wxTextCtrl* m_log;
};
class GridRenderersApp : public wxApp
{
public:
virtual bool OnInit() override
{
GridRenderersFrame* frame = new GridRenderersFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(GridRenderersApp);

View file

@ -0,0 +1,346 @@
// wxHtmlWindow Test - Tests HTML Window in WASM
// KiCad uses wxHtmlWindow for About dialogs, error formatting, and descriptions
// instead of wxRichTextCtrl
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/html/htmlwin.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class HtmlWinTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class HtmlWinTestFrame : public wxFrame
{
public:
HtmlWinTestFrame();
private:
wxHtmlWindow* m_htmlWin;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void SetBasicContent();
void SetTableContent();
void SetLongContent();
void OnLinkClicked(wxHtmlLinkEvent& evt);
void OnBasicContent(wxCommandEvent& evt);
void OnTableContent(wxCommandEvent& evt);
void OnLongContent(wxCommandEvent& evt);
void OnKiCadAbout(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_HTML = wxID_HIGHEST + 1,
ID_BASIC_CONTENT,
ID_TABLE_CONTENT,
ID_LONG_CONTENT,
ID_KICAD_ABOUT
};
wxBEGIN_EVENT_TABLE(HtmlWinTestFrame, wxFrame)
EVT_HTML_LINK_CLICKED(ID_HTML, HtmlWinTestFrame::OnLinkClicked)
EVT_BUTTON(ID_BASIC_CONTENT, HtmlWinTestFrame::OnBasicContent)
EVT_BUTTON(ID_TABLE_CONTENT, HtmlWinTestFrame::OnTableContent)
EVT_BUTTON(ID_LONG_CONTENT, HtmlWinTestFrame::OnLongContent)
EVT_BUTTON(ID_KICAD_ABOUT, HtmlWinTestFrame::OnKiCadAbout)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(HtmlWinTestApp);
bool HtmlWinTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
HtmlWinTestFrame* frame = new HtmlWinTestFrame();
frame->Show(true);
return true;
}
HtmlWinTestFrame::HtmlWinTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxHtmlWindow WASM Test",
wxDefaultPosition, wxSize(700, 650))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxHtmlWindow Test\n\n"
"KiCad uses HtmlWindow for About dialogs, error messages, and symbol descriptions.\n"
"Click buttons to load different HTML content.");
mainSizer->Add(desc, 0, wxALL, 10);
// Button bar
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_BASIC_CONTENT, "Basic HTML"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_TABLE_CONTENT, "Tables"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_LONG_CONTENT, "Long Content"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_KICAD_ABOUT, "KiCad-style About"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
// HTML Window
m_htmlWin = new wxHtmlWindow(this, ID_HTML, wxDefaultPosition, wxSize(-1, 300),
wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER);
mainSizer->Add(m_htmlWin, 1, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
// Set initial content
SetBasicContent();
LogEvent("HtmlWindow test app started");
LogEvent("Initial content loaded");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[HTMLWIN_TEST] wxHtmlWindow test app started successfully');
});
#endif
}
void HtmlWinTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[HTMLWIN_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void HtmlWinTestFrame::SetBasicContent()
{
wxString html = R"(
<html>
<body>
<h1>Basic HTML Test</h1>
<p>This tests <b>bold</b>, <i>italic</i>, and <u>underlined</u> text.</p>
<h2>Lists</h2>
<ul>
<li>Unordered item 1</li>
<li>Unordered item 2</li>
<li>Unordered item 3</li>
</ul>
<ol>
<li>Ordered item 1</li>
<li>Ordered item 2</li>
<li>Ordered item 3</li>
</ol>
<h2>Links</h2>
<p>Click this <a href="test://link1">test link</a> to fire an event.</p>
<p>Another <a href="test://link2">second link</a> for testing.</p>
<h2>Colors</h2>
<p><font color="red">Red text</font>,
<font color="green">green text</font>,
<font color="blue">blue text</font>.</p>
<h2>Horizontal Rule</h2>
<hr>
<p>Content below the line.</p>
</body>
</html>
)";
m_htmlWin->SetPage(html);
LogEvent("Loaded basic HTML content");
}
void HtmlWinTestFrame::SetTableContent()
{
wxString html = R"(
<html>
<body>
<h1>Table Test</h1>
<p>This tests HTML tables similar to KiCad's component info display.</p>
<h2>Component Properties</h2>
<table border="1" cellpadding="5">
<tr bgcolor="#CCCCCC">
<th>Property</th>
<th>Value</th>
</tr>
<tr>
<td>Reference</td>
<td>U1</td>
</tr>
<tr>
<td>Value</td>
<td>STM32F103C8</td>
</tr>
<tr>
<td>Footprint</td>
<td>LQFP-48</td>
</tr>
<tr>
<td>Datasheet</td>
<td><a href="test://datasheet">View PDF</a></td>
</tr>
</table>
<h2>Pin Table</h2>
<table border="1" cellpadding="3">
<tr bgcolor="#E0E0E0">
<th>Pin</th>
<th>Name</th>
<th>Type</th>
<th>Net</th>
</tr>
<tr>
<td>1</td>
<td>VCC</td>
<td>Power</td>
<td>+3V3</td>
</tr>
<tr>
<td>2</td>
<td>GND</td>
<td>Power</td>
<td>GND</td>
</tr>
<tr>
<td>3</td>
<td>PA0</td>
<td>I/O</td>
<td>Net1</td>
</tr>
<tr>
<td>4</td>
<td>PA1</td>
<td>I/O</td>
<td>Net2</td>
</tr>
</table>
</body>
</html>
)";
m_htmlWin->SetPage(html);
LogEvent("Loaded table HTML content");
}
void HtmlWinTestFrame::SetLongContent()
{
wxString html = R"(<html><body>
<h1>Long Scrollable Content</h1>
<p>This tests scrolling behavior with long content.</p>
)";
// Generate long content
for (int i = 1; i <= 30; i++) {
html += wxString::Format(
"<h3>Section %d</h3>\n"
"<p>This is paragraph %d of the long content test. "
"It contains enough text to verify scrolling works correctly "
"in the wxHtmlWindow WASM implementation.</p>\n",
i, i
);
}
html += "</body></html>";
m_htmlWin->SetPage(html);
LogEvent("Loaded long scrollable content (30 sections)");
}
void HtmlWinTestFrame::OnLinkClicked(wxHtmlLinkEvent& evt)
{
wxString href = evt.GetLinkInfo().GetHref();
LogEvent(wxString::Format("Link clicked: '%s'", href));
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[HTMLWIN_LINK] Link clicked: ' + UTF8ToString($0));
}, href.c_str().AsChar());
#endif
}
void HtmlWinTestFrame::OnBasicContent(wxCommandEvent& WXUNUSED(evt))
{
SetBasicContent();
}
void HtmlWinTestFrame::OnTableContent(wxCommandEvent& WXUNUSED(evt))
{
SetTableContent();
}
void HtmlWinTestFrame::OnLongContent(wxCommandEvent& WXUNUSED(evt))
{
SetLongContent();
}
void HtmlWinTestFrame::OnKiCadAbout(wxCommandEvent& WXUNUSED(evt))
{
wxString html = R"(
<html>
<body>
<center>
<h1>KiCad EDA</h1>
<p><b>Version 8.0.0</b></p>
<p>An open source EDA suite for schematic capture<br>
and PCB design.</p>
<hr width="50%">
<table border="0">
<tr>
<td align="right"><b>Build:</b></td>
<td>WASM (Emscripten)</td>
</tr>
<tr>
<td align="right"><b>Platform:</b></td>
<td>Web Browser</td>
</tr>
<tr>
<td align="right"><b>wxWidgets:</b></td>
<td>3.3.0</td>
</tr>
</table>
<hr width="50%">
<h3>Libraries</h3>
<p>
<a href="test://wxwidgets">wxWidgets</a> |
<a href="test://boost">Boost</a> |
<a href="test://opencascade">OpenCASCADE</a>
</p>
<h3>License</h3>
<p>KiCad is free software licensed under the<br>
<a href="test://gpl">GNU General Public License v3</a></p>
<p><font size="-1">Copyright (c) 2024 KiCad Developers</font></p>
</center>
</body>
</html>
)";
m_htmlWin->SetPage(html);
LogEvent("Loaded KiCad-style About content");
}

View file

@ -0,0 +1,204 @@
// wxInfoBar Test - Tests info bar in WASM
// KiCad uses info bars for notifications and warnings
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/infobar.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class InfoBarTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class InfoBarTestFrame : public wxFrame
{
public:
InfoBarTestFrame();
private:
wxInfoBar* m_infoBar;
wxTextCtrl* m_log;
wxPanel* m_contentPanel;
void LogEvent(const wxString& msg);
void OnShowInfo(wxCommandEvent& evt);
void OnShowWarning(wxCommandEvent& evt);
void OnShowError(wxCommandEvent& evt);
void OnShowWithButton(wxCommandEvent& evt);
void OnDismiss(wxCommandEvent& evt);
void OnInfoBarButton(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_SHOW_INFO = wxID_HIGHEST + 1,
ID_SHOW_WARNING,
ID_SHOW_ERROR,
ID_SHOW_WITH_BUTTON,
ID_DISMISS,
ID_INFOBAR_BUTTON
};
wxBEGIN_EVENT_TABLE(InfoBarTestFrame, wxFrame)
EVT_BUTTON(ID_SHOW_INFO, InfoBarTestFrame::OnShowInfo)
EVT_BUTTON(ID_SHOW_WARNING, InfoBarTestFrame::OnShowWarning)
EVT_BUTTON(ID_SHOW_ERROR, InfoBarTestFrame::OnShowError)
EVT_BUTTON(ID_SHOW_WITH_BUTTON, InfoBarTestFrame::OnShowWithButton)
EVT_BUTTON(ID_DISMISS, InfoBarTestFrame::OnDismiss)
EVT_BUTTON(ID_INFOBAR_BUTTON, InfoBarTestFrame::OnInfoBarButton)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(InfoBarTestApp);
bool InfoBarTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
InfoBarTestFrame* frame = new InfoBarTestFrame();
frame->Show(true);
return true;
}
InfoBarTestFrame::InfoBarTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxInfoBar WASM Test",
wxDefaultPosition, wxSize(700, 500))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Info bar at the top (like KiCad)
m_infoBar = new wxInfoBar(this);
mainSizer->Add(m_infoBar, 0, wxEXPAND);
// Description
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxInfoBar Test\n\n"
"KiCad uses wxInfoBar for notifications, warnings, and error messages.\n"
"Click the buttons below to show different types of messages.");
mainSizer->Add(desc, 0, wxALL, 10);
// Button panel
wxStaticBoxSizer* btnBox = new wxStaticBoxSizer(wxVERTICAL, this, "Show Messages");
wxFlexGridSizer* btnGrid = new wxFlexGridSizer(2, 10, 10);
btnGrid->Add(new wxButton(this, ID_SHOW_INFO, "Show Info Message"), 0, wxEXPAND);
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Blue info bar with info icon"), 0, wxALIGN_CENTER_VERTICAL);
btnGrid->Add(new wxButton(this, ID_SHOW_WARNING, "Show Warning Message"), 0, wxEXPAND);
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Yellow warning bar with warning icon"), 0, wxALIGN_CENTER_VERTICAL);
btnGrid->Add(new wxButton(this, ID_SHOW_ERROR, "Show Error Message"), 0, wxEXPAND);
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Red error bar with error icon"), 0, wxALIGN_CENTER_VERTICAL);
btnGrid->Add(new wxButton(this, ID_SHOW_WITH_BUTTON, "Show With Action Button"), 0, wxEXPAND);
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Info bar with clickable action button"), 0, wxALIGN_CENTER_VERTICAL);
btnGrid->Add(new wxButton(this, ID_DISMISS, "Dismiss"), 0, wxEXPAND);
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Hide the info bar"), 0, wxALIGN_CENTER_VERTICAL);
btnBox->Add(btnGrid, 0, wxALL, 10);
mainSizer->Add(btnBox, 0, wxEXPAND | wxALL, 10);
// Sample KiCad-like messages box
wxStaticBoxSizer* exampleBox = new wxStaticBoxSizer(wxVERTICAL, this, "Example KiCad Messages");
wxArrayString examples;
examples.Add("INFO: Board successfully loaded from 'myproject.kicad_pcb'");
examples.Add("WARNING: Footprint 'R_0402' not found in library, using fallback");
examples.Add("ERROR: DRC violation: Clearance 0.15mm < 0.2mm required");
examples.Add("INFO: Design rules check completed: 0 errors, 2 warnings");
examples.Add("WARNING: Component 'U3' has unconnected pins: 12, 14, 15");
wxListBox* exampleList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100),
examples, wxLB_SINGLE);
exampleBox->Add(exampleList, 0, wxEXPAND | wxALL, 5);
mainSizer->Add(exampleBox, 0, 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 - wxInfoBar test");
LogEvent("InfoBar test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[INFOBAR_TEST] wxInfoBar test app started successfully');
});
#endif
}
void InfoBarTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[INFOBAR_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void InfoBarTestFrame::OnShowInfo(wxCommandEvent& WXUNUSED(evt))
{
m_infoBar->ShowMessage("Board successfully loaded. Design contains 245 components and 89 nets.",
wxICON_INFORMATION);
LogEvent("Showed info message");
}
void InfoBarTestFrame::OnShowWarning(wxCommandEvent& WXUNUSED(evt))
{
m_infoBar->ShowMessage("Warning: Some footprints could not be found in the configured libraries.",
wxICON_WARNING);
LogEvent("Showed warning message");
}
void InfoBarTestFrame::OnShowError(wxCommandEvent& WXUNUSED(evt))
{
m_infoBar->ShowMessage("Error: DRC check failed. 3 clearance violations found.",
wxICON_ERROR);
LogEvent("Showed error message");
}
void InfoBarTestFrame::OnShowWithButton(wxCommandEvent& WXUNUSED(evt))
{
m_infoBar->RemoveButton(ID_INFOBAR_BUTTON);
m_infoBar->AddButton(ID_INFOBAR_BUTTON, "View Details");
m_infoBar->ShowMessage("ERC completed with 2 warnings. Click to view details.",
wxICON_WARNING);
LogEvent("Showed message with action button");
}
void InfoBarTestFrame::OnDismiss(wxCommandEvent& WXUNUSED(evt))
{
m_infoBar->Dismiss();
LogEvent("Info bar dismissed");
}
void InfoBarTestFrame::OnInfoBarButton(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Info bar action button clicked - would open details dialog");
wxMessageBox("This would open the ERC results dialog in KiCad.",
"Action Button Clicked", wxOK | wxICON_INFORMATION);
}

View file

@ -0,0 +1,142 @@
// Layout Test - Tests wxSplitterWindow and wxScrolledWindow in WASM
// KiCad uses splitters and scrolled windows extensively
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/splitter.h"
#include "wx/scrolwin.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class LayoutTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class LayoutTestFrame : public wxFrame
{
public:
LayoutTestFrame();
private:
wxSplitterWindow* m_splitter;
wxScrolledWindow* m_scrollLeft;
wxScrolledWindow* m_scrollRight;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnSplitterSashPosChanged(wxSplitterEvent& evt);
void OnScrollWin(wxScrollWinEvent& evt);
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(LayoutTestFrame, wxFrame)
EVT_SPLITTER_SASH_POS_CHANGED(wxID_ANY, LayoutTestFrame::OnSplitterSashPosChanged)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(LayoutTestApp);
bool LayoutTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
LayoutTestFrame* frame = new LayoutTestFrame();
frame->Show(true);
return true;
}
LayoutTestFrame::LayoutTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxSplitter/wxScrolled WASM Test",
wxDefaultPosition, wxSize(800, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxSplitterWindow and wxScrolledWindow Test - KiCad layout controls");
mainSizer->Add(desc, 0, wxALL, 5);
// Create splitter
m_splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE);
// Left scrolled window
m_scrollLeft = new wxScrolledWindow(m_splitter, wxID_ANY);
m_scrollLeft->SetBackgroundColour(*wxLIGHT_GREY);
m_scrollLeft->SetScrollbars(10, 10, 100, 100);
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
for (int i = 0; i < 20; i++) {
leftSizer->Add(new wxStaticText(m_scrollLeft, wxID_ANY,
wxString::Format("Left Item %d", i+1)), 0, wxALL, 5);
}
m_scrollLeft->SetSizer(leftSizer);
// Right scrolled window
m_scrollRight = new wxScrolledWindow(m_splitter, wxID_ANY);
m_scrollRight->SetBackgroundColour(*wxWHITE);
m_scrollRight->SetScrollbars(10, 10, 100, 100);
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
for (int i = 0; i < 20; i++) {
rightSizer->Add(new wxStaticText(m_scrollRight, wxID_ANY,
wxString::Format("Right Item %d", i+1)), 0, wxALL, 5);
}
m_scrollRight->SetSizer(rightSizer);
m_splitter->SplitVertically(m_scrollLeft, m_scrollRight, 300);
m_splitter->SetMinimumPaneSize(100);
mainSizer->Add(m_splitter, 1, wxEXPAND | wxALL, 5);
// 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, 5);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Drag splitter sash or scroll the panes");
LogEvent("Layout test app started");
LogEvent("Splitter position: 300");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[LAYOUT_TEST] wxSplitter/wxScrolled test app started successfully');
});
#endif
}
void LayoutTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[LAYOUT_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void LayoutTestFrame::OnSplitterSashPosChanged(wxSplitterEvent& evt)
{
LogEvent(wxString::Format("Splitter sash moved to: %d", evt.GetSashPosition()));
}
void LayoutTestFrame::OnScrollWin(wxScrollWinEvent& evt)
{
LogEvent("Scroll event");
evt.Skip();
}

View file

@ -0,0 +1,335 @@
// 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));
}

View file

@ -0,0 +1,215 @@
// wxLogError Dialog Test - Reproduces KiCad's kiface error dialog
// Tests wxLogDialog with wxCollapsiblePane "Details" dropdown
// Used to debug dialog positioning and wxLog console logging
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/log.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class LogErrorTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class LogErrorTestFrame : public wxFrame
{
public:
LogErrorTestFrame();
private:
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
// Single error - like KiCad's "Error loading editor."
void OnSingleError(wxCommandEvent& evt);
// Multiple errors - triggers Details dropdown with wxListCtrl
void OnMultipleErrors(wxCommandEvent& evt);
// Mix of error levels
void OnMixedLevels(wxCommandEvent& evt);
// Manually flush the log to show dialog
void OnFlushLog(wxCommandEvent& evt);
// Clear logged messages without showing dialog
void OnClearLog(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_SINGLE_ERROR = wxID_HIGHEST + 1,
ID_MULTIPLE_ERRORS,
ID_MIXED_LEVELS,
ID_FLUSH_LOG,
ID_CLEAR_LOG
};
wxBEGIN_EVENT_TABLE(LogErrorTestFrame, wxFrame)
EVT_BUTTON(ID_SINGLE_ERROR, LogErrorTestFrame::OnSingleError)
EVT_BUTTON(ID_MULTIPLE_ERRORS, LogErrorTestFrame::OnMultipleErrors)
EVT_BUTTON(ID_MIXED_LEVELS, LogErrorTestFrame::OnMixedLevels)
EVT_BUTTON(ID_FLUSH_LOG, LogErrorTestFrame::OnFlushLog)
EVT_BUTTON(ID_CLEAR_LOG, LogErrorTestFrame::OnClearLog)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(LogErrorTestApp);
bool LogErrorTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
LogErrorTestFrame* frame = new LogErrorTestFrame();
frame->Show(true);
return true;
}
LogErrorTestFrame::LogErrorTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxLogError Dialog Test",
wxDefaultPosition, wxSize(700, 550))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxLogError Dialog Test\n\n"
"This test reproduces the exact dialog KiCad shows when kiface loading fails.\n"
"It uses wxLogDialog with wxCollapsiblePane 'Details' dropdown.\n"
"Watch the browser console for [wxLog] messages.");
mainSizer->Add(desc, 0, wxALL, 10);
// Single error section - like KiCad's error
wxStaticBoxSizer* singleBox = new wxStaticBoxSizer(wxVERTICAL, this, "Single Error (KiCad-style)");
wxBoxSizer* singleBtnSizer = new wxBoxSizer(wxHORIZONTAL);
singleBtnSizer->Add(new wxButton(this, ID_SINGLE_ERROR, "Trigger Error"), 0, wxALL, 5);
singleBtnSizer->Add(new wxStaticText(this, wxID_ANY, "Calls wxLogError(\"Error loading editor.\")"),
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
singleBox->Add(singleBtnSizer, 0, wxALIGN_LEFT);
mainSizer->Add(singleBox, 0, wxEXPAND | wxALL, 10);
// Multiple errors section - triggers Details dropdown
wxStaticBoxSizer* multiBox = new wxStaticBoxSizer(wxVERTICAL, this, "Multiple Errors (Details dropdown)");
wxBoxSizer* multiBtnSizer = new wxBoxSizer(wxHORIZONTAL);
multiBtnSizer->Add(new wxButton(this, ID_MULTIPLE_ERRORS, "Trigger Multiple"), 0, wxALL, 5);
multiBtnSizer->Add(new wxStaticText(this, wxID_ANY, "Logs 3 errors - shows Details with wxListCtrl"),
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
multiBox->Add(multiBtnSizer, 0, wxALIGN_LEFT);
mainSizer->Add(multiBox, 0, wxEXPAND | wxALL, 10);
// Mixed levels section
wxStaticBoxSizer* mixedBox = new wxStaticBoxSizer(wxVERTICAL, this, "Mixed Log Levels");
wxBoxSizer* mixedBtnSizer = new wxBoxSizer(wxHORIZONTAL);
mixedBtnSizer->Add(new wxButton(this, ID_MIXED_LEVELS, "Mixed Levels"), 0, wxALL, 5);
mixedBtnSizer->Add(new wxStaticText(this, wxID_ANY, "wxLogError + wxLogWarning + wxLogMessage"),
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
mixedBox->Add(mixedBtnSizer, 0, wxALIGN_LEFT);
mainSizer->Add(mixedBox, 0, wxEXPAND | wxALL, 10);
// Control buttons
wxStaticBoxSizer* controlBox = new wxStaticBoxSizer(wxHORIZONTAL, this, "Log Control");
controlBox->Add(new wxButton(this, ID_FLUSH_LOG, "Flush Log (Show Dialog)"), 0, wxALL, 5);
controlBox->Add(new wxButton(this, ID_CLEAR_LOG, "Clear Log"), 0, wxALL, 5);
mainSizer->Add(controlBox, 0, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready - Click buttons to trigger wxLog errors");
LogEvent("wxLogError test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[LOGERROR_TEST] wxLogError test app started successfully');
});
#endif
}
void LogErrorTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[LOGERROR_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void LogErrorTestFrame::OnSingleError(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Triggering single wxLogError...");
// This is exactly what KiCad does in kiway.cpp
wxLogError("Error loading editor.");
LogEvent("wxLogError called - dialog should appear on next event loop or Flush");
}
void LogErrorTestFrame::OnMultipleErrors(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Triggering multiple wxLogError calls...");
// Multiple errors trigger the Details dropdown with wxListCtrl
wxLogError("Failed to load shared library '/usr/bin/_pcbnew.kiface'");
wxLogError("IO_ERROR: Failed to load kiface library");
wxLogError("Error loading editor.");
LogEvent("3 errors logged - Details dropdown should appear");
}
void LogErrorTestFrame::OnMixedLevels(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Triggering mixed log levels...");
wxLogError("This is an error message");
wxLogWarning("This is a warning message");
wxLogMessage("This is an info message");
LogEvent("Mixed levels logged - check console for [wxLog] output");
}
void LogErrorTestFrame::OnFlushLog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Flushing log - dialog should appear now...");
// Force the log to flush, which shows the dialog
wxLog::FlushActive();
LogEvent("Flush complete - dialog should have been shown");
}
void LogErrorTestFrame::OnClearLog(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Clearing accumulated log messages...");
// Get the active log and clear it without showing dialog
wxLog* log = wxLog::GetActiveTarget();
if (log)
{
// Disable logging temporarily to clear without showing
wxLogNull noLog;
// The accumulated messages will be discarded
}
LogEvent("Log cleared");
}

View file

@ -0,0 +1,179 @@
// Maximize Test - Tests wxFrame::Maximize() which KiCad uses
// This reproduces the bug where Maximize() returns a tiny window when
// GetScreenSize() returns incorrect values at startup.
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/display.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class MaximizeTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class MaximizeTestFrame : public wxFrame
{
public:
MaximizeTestFrame();
private:
wxStaticText* m_sizeLabel;
wxStaticText* m_displayLabel;
void UpdateSizeDisplay();
void OnSize(wxSizeEvent& evt);
void OnMaximize(wxCommandEvent& evt);
void OnRestore(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_MAXIMIZE = wxID_HIGHEST + 1,
ID_RESTORE
};
wxBEGIN_EVENT_TABLE(MaximizeTestFrame, wxFrame)
EVT_SIZE(MaximizeTestFrame::OnSize)
EVT_BUTTON(ID_MAXIMIZE, MaximizeTestFrame::OnMaximize)
EVT_BUTTON(ID_RESTORE, MaximizeTestFrame::OnRestore)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(MaximizeTestApp);
bool MaximizeTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
// Create frame WITHOUT explicit size - uses wxDefaultSize
// This is what KiCad does
MaximizeTestFrame* frame = new MaximizeTestFrame();
// KiCad calls Maximize() after creation
frame->Maximize(true);
frame->Show(true);
return true;
}
MaximizeTestFrame::MaximizeTestFrame()
: wxFrame(nullptr, wxID_ANY, "Maximize Test",
wxDefaultPosition, wxDefaultSize) // No explicit size!
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"Maximize Test\n\n"
"This test reproduces the KiCad startup issue.\n"
"The frame is created with wxDefaultSize and Maximize() is called.\n"
"If display size detection works, the window should be fullscreen.");
mainSizer->Add(desc, 0, wxALL, 10);
// Display info
wxStaticBoxSizer* infoBox = new wxStaticBoxSizer(wxVERTICAL, this, "Display Info");
m_displayLabel = new wxStaticText(this, wxID_ANY, "Display: querying...");
infoBox->Add(m_displayLabel, 0, wxALL, 5);
m_sizeLabel = new wxStaticText(this, wxID_ANY, "Window size: querying...");
infoBox->Add(m_sizeLabel, 0, wxALL, 5);
mainSizer->Add(infoBox, 0, wxEXPAND | wxALL, 10);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_MAXIMIZE, "Maximize"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_RESTORE, "Restore"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 10);
SetSizer(mainSizer);
// Query display info
wxDisplay display(wxDisplay::GetFromWindow(this));
wxRect geom = display.GetGeometry();
wxRect client = display.GetClientArea();
wxString displayInfo = wxString::Format(
"Display geometry: %dx%d, Client area: %dx%d",
geom.GetWidth(), geom.GetHeight(),
client.GetWidth(), client.GetHeight());
m_displayLabel->SetLabel(displayInfo);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[MAXIMIZE_TEST] Display geometry: ' + $0 + 'x' + $1);
console.log('[MAXIMIZE_TEST] Client area: ' + $2 + 'x' + $3);
}, geom.GetWidth(), geom.GetHeight(), client.GetWidth(), client.GetHeight());
#endif
// Update size display after creation
CallAfter(&MaximizeTestFrame::UpdateSizeDisplay);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[MAXIMIZE_TEST] Maximize test app started');
});
#endif
}
void MaximizeTestFrame::UpdateSizeDisplay()
{
wxSize size = GetSize();
wxSize clientSize = GetClientSize();
wxString sizeInfo = wxString::Format(
"Window size: %dx%d, Client: %dx%d, Maximized: %s",
size.GetWidth(), size.GetHeight(),
clientSize.GetWidth(), clientSize.GetHeight(),
IsMaximized() ? "YES" : "NO");
m_sizeLabel->SetLabel(sizeInfo);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[MAXIMIZE_TEST] Window size: ' + $0 + 'x' + $1);
console.log('[MAXIMIZE_TEST] Client size: ' + $2 + 'x' + $3);
console.log('[MAXIMIZE_TEST] Is maximized: ' + ($4 ? 'YES' : 'NO'));
// Test assertion: window should be larger than 100px if maximize worked
if ($0 < 100 || $1 < 100) {
console.error('[MAXIMIZE_TEST] FAIL: Window is too small! Expected fullscreen, got ' + $0 + 'x' + $1);
} else {
console.log('[MAXIMIZE_TEST] PASS: Window size is reasonable');
}
}, size.GetWidth(), size.GetHeight(),
clientSize.GetWidth(), clientSize.GetHeight(),
IsMaximized() ? 1 : 0);
#endif
}
void MaximizeTestFrame::OnSize(wxSizeEvent& evt)
{
evt.Skip();
CallAfter(&MaximizeTestFrame::UpdateSizeDisplay);
}
void MaximizeTestFrame::OnMaximize(wxCommandEvent& WXUNUSED(evt))
{
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[MAXIMIZE_TEST] Maximize button clicked'); });
#endif
Maximize(true);
}
void MaximizeTestFrame::OnRestore(wxCommandEvent& WXUNUSED(evt))
{
#ifdef __EMSCRIPTEN__
EM_ASM({ console.log('[MAXIMIZE_TEST] Restore button clicked'); });
#endif
Maximize(false);
}

View file

@ -0,0 +1,300 @@
// wxMenuBar Test - Tests menu functionality in WASM
// KiCad uses extensive menus for File, Edit, View, Place, Route, etc.
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class MenuTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class MenuTestFrame : public wxFrame
{
public:
MenuTestFrame();
private:
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
// Menu event handlers
void OnMenuNew(wxCommandEvent& evt);
void OnMenuOpen(wxCommandEvent& evt);
void OnMenuSave(wxCommandEvent& evt);
void OnMenuSaveAs(wxCommandEvent& evt);
void OnMenuExit(wxCommandEvent& evt);
void OnMenuUndo(wxCommandEvent& evt);
void OnMenuRedo(wxCommandEvent& evt);
void OnMenuCut(wxCommandEvent& evt);
void OnMenuCopy(wxCommandEvent& evt);
void OnMenuPaste(wxCommandEvent& evt);
void OnMenuSelectAll(wxCommandEvent& evt);
void OnMenuZoomIn(wxCommandEvent& evt);
void OnMenuZoomOut(wxCommandEvent& evt);
void OnMenuZoomFit(wxCommandEvent& evt);
void OnMenuFullScreen(wxCommandEvent& evt);
void OnMenuPreferences(wxCommandEvent& evt);
void OnMenuAbout(wxCommandEvent& evt);
void OnMenuHelp(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_NEW = wxID_HIGHEST + 1,
ID_OPEN,
ID_SAVE,
ID_SAVE_AS,
ID_UNDO,
ID_REDO,
ID_CUT,
ID_COPY,
ID_PASTE,
ID_SELECT_ALL,
ID_ZOOM_IN,
ID_ZOOM_OUT,
ID_ZOOM_FIT,
ID_FULLSCREEN,
ID_PREFERENCES,
ID_HELP_CONTENTS
};
wxBEGIN_EVENT_TABLE(MenuTestFrame, wxFrame)
EVT_MENU(ID_NEW, MenuTestFrame::OnMenuNew)
EVT_MENU(ID_OPEN, MenuTestFrame::OnMenuOpen)
EVT_MENU(ID_SAVE, MenuTestFrame::OnMenuSave)
EVT_MENU(ID_SAVE_AS, MenuTestFrame::OnMenuSaveAs)
EVT_MENU(wxID_EXIT, MenuTestFrame::OnMenuExit)
EVT_MENU(ID_UNDO, MenuTestFrame::OnMenuUndo)
EVT_MENU(ID_REDO, MenuTestFrame::OnMenuRedo)
EVT_MENU(ID_CUT, MenuTestFrame::OnMenuCut)
EVT_MENU(ID_COPY, MenuTestFrame::OnMenuCopy)
EVT_MENU(ID_PASTE, MenuTestFrame::OnMenuPaste)
EVT_MENU(ID_SELECT_ALL, MenuTestFrame::OnMenuSelectAll)
EVT_MENU(ID_ZOOM_IN, MenuTestFrame::OnMenuZoomIn)
EVT_MENU(ID_ZOOM_OUT, MenuTestFrame::OnMenuZoomOut)
EVT_MENU(ID_ZOOM_FIT, MenuTestFrame::OnMenuZoomFit)
EVT_MENU(ID_FULLSCREEN, MenuTestFrame::OnMenuFullScreen)
EVT_MENU(ID_PREFERENCES, MenuTestFrame::OnMenuPreferences)
EVT_MENU(wxID_ABOUT, MenuTestFrame::OnMenuAbout)
EVT_MENU(ID_HELP_CONTENTS, MenuTestFrame::OnMenuHelp)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(MenuTestApp);
bool MenuTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
MenuTestFrame* frame = new MenuTestFrame();
frame->Show(true);
return true;
}
MenuTestFrame::MenuTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxMenuBar WASM Test",
wxDefaultPosition, wxSize(600, 400))
{
// Create File menu
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_NEW, "&New\tCtrl+N", "Create a new file");
menuFile->Append(ID_OPEN, "&Open...\tCtrl+O", "Open an existing file");
menuFile->AppendSeparator();
menuFile->Append(ID_SAVE, "&Save\tCtrl+S", "Save the current file");
menuFile->Append(ID_SAVE_AS, "Save &As...\tCtrl+Shift+S", "Save with a new name");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT, "E&xit\tAlt+F4", "Exit the application");
// Create Edit menu
wxMenu* menuEdit = new wxMenu;
menuEdit->Append(ID_UNDO, "&Undo\tCtrl+Z", "Undo the last action");
menuEdit->Append(ID_REDO, "&Redo\tCtrl+Y", "Redo the last undone action");
menuEdit->AppendSeparator();
menuEdit->Append(ID_CUT, "Cu&t\tCtrl+X", "Cut selection to clipboard");
menuEdit->Append(ID_COPY, "&Copy\tCtrl+C", "Copy selection to clipboard");
menuEdit->Append(ID_PASTE, "&Paste\tCtrl+V", "Paste from clipboard");
menuEdit->AppendSeparator();
menuEdit->Append(ID_SELECT_ALL, "Select &All\tCtrl+A", "Select all");
// Create View menu
wxMenu* menuView = new wxMenu;
menuView->Append(ID_ZOOM_IN, "Zoom &In\tCtrl++", "Zoom in");
menuView->Append(ID_ZOOM_OUT, "Zoom &Out\tCtrl+-", "Zoom out");
menuView->Append(ID_ZOOM_FIT, "Zoom to &Fit\tCtrl+0", "Fit view to window");
menuView->AppendSeparator();
menuView->AppendCheckItem(ID_FULLSCREEN, "&Full Screen\tF11", "Toggle full screen mode");
// Create Tools menu (like KiCad's preferences)
wxMenu* menuTools = new wxMenu;
menuTools->Append(ID_PREFERENCES, "&Preferences...", "Open preferences dialog");
// Create Help menu
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(ID_HELP_CONTENTS, "&Help Contents\tF1", "Show help");
menuHelp->AppendSeparator();
menuHelp->Append(wxID_ABOUT, "&About...", "About this application");
// Create menu bar
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuEdit, "&Edit");
menuBar->Append(menuView, "&View");
menuBar->Append(menuTools, "&Tools");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
// Create main content - NO GL canvas
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxMenuBar Test\n\n"
"This tests the menu system which KiCad uses extensively.\n"
"Click menu items to see events logged below.");
sizer->Add(desc, 0, wxALL, 10);
m_log = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 200),
wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(m_log, 1, wxEXPAND | wxALL, 10);
SetSizer(sizer);
// Create status bar
CreateStatusBar(2);
SetStatusText("Ready");
SetStatusText("Menu test", 1);
LogEvent("Menu test app started");
LogEvent("Menu bar created with File, Edit, View, Tools, Help menus");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[MENU_TEST] wxMenuBar test app started successfully');
});
#endif
}
void MenuTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[MENU_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
// File menu handlers
void MenuTestFrame::OnMenuNew(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("File > New clicked");
}
void MenuTestFrame::OnMenuOpen(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("File > Open clicked");
}
void MenuTestFrame::OnMenuSave(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("File > Save clicked");
}
void MenuTestFrame::OnMenuSaveAs(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("File > Save As clicked");
}
void MenuTestFrame::OnMenuExit(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("File > Exit clicked");
Close(true);
}
// Edit menu handlers
void MenuTestFrame::OnMenuUndo(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Undo clicked");
}
void MenuTestFrame::OnMenuRedo(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Redo clicked");
}
void MenuTestFrame::OnMenuCut(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Cut clicked");
}
void MenuTestFrame::OnMenuCopy(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Copy clicked");
}
void MenuTestFrame::OnMenuPaste(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Paste clicked");
}
void MenuTestFrame::OnMenuSelectAll(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Edit > Select All clicked");
}
// View menu handlers
void MenuTestFrame::OnMenuZoomIn(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("View > Zoom In clicked");
}
void MenuTestFrame::OnMenuZoomOut(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("View > Zoom Out clicked");
}
void MenuTestFrame::OnMenuZoomFit(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("View > Zoom to Fit clicked");
}
void MenuTestFrame::OnMenuFullScreen(wxCommandEvent& evt)
{
bool isFullScreen = evt.IsChecked();
LogEvent(wxString::Format("View > Full Screen toggled: %s",
isFullScreen ? "ON" : "OFF"));
ShowFullScreen(isFullScreen);
}
// Tools menu handlers
void MenuTestFrame::OnMenuPreferences(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Tools > Preferences clicked");
}
// Help menu handlers
void MenuTestFrame::OnMenuAbout(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Help > About clicked");
}
void MenuTestFrame::OnMenuHelp(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Help > Help Contents clicked");
}

View file

@ -0,0 +1,330 @@
// wxOwnerDrawnComboBox Test - Custom dropdown rendering like KiCad's layer/font selectors
// Tests: wxOwnerDrawnComboBox, OnDrawItem, OnMeasureItem, custom rendering
#include "wx/wx.h"
#include "wx/odcombo.h"
#include "wx/dcmemory.h"
// Custom owner-drawn combo box for layer selection (like KiCad LAYER_BOX_SELECTOR)
class LayerComboBox : public wxOwnerDrawnComboBox
{
public:
LayerComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
wxSize(200, -1), 0, nullptr, wxCB_READONLY)
{
// Add layers with colors
Append("F.Cu"); m_colors.push_back(*wxRED);
Append("B.Cu"); m_colors.push_back(*wxBLUE);
Append("F.SilkS"); m_colors.push_back(*wxYELLOW);
Append("B.SilkS"); m_colors.push_back(wxColour(255, 0, 255));
Append("F.Mask"); m_colors.push_back(wxColour(0, 128, 0));
Append("B.Mask"); m_colors.push_back(wxColour(0, 128, 128));
Append("Edge.Cuts"); m_colors.push_back(*wxWHITE);
Append("Dwgs.User"); m_colors.push_back(wxColour(128, 128, 128));
SetSelection(0);
}
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
{
if (item == wxNOT_FOUND)
return;
// Draw background
if (flags & wxODCB_PAINTING_SELECTED)
{
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rect);
dc.SetTextForeground(*wxWHITE);
}
else
{
dc.SetTextForeground(*wxBLACK);
}
// Draw color swatch
wxRect swatchRect(rect.x + 4, rect.y + 4, 20, rect.height - 8);
dc.SetBrush(wxBrush(m_colors[item]));
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(swatchRect);
// Draw text
wxString text = GetString(item);
dc.DrawText(text, rect.x + 30, rect.y + (rect.height - dc.GetCharHeight()) / 2);
}
virtual wxCoord OnMeasureItem(size_t item) const override
{
return 24; // Fixed item height
}
virtual wxCoord OnMeasureItemWidth(size_t item) const override
{
return -1; // Use default width
}
private:
std::vector<wxColour> m_colors;
};
// Custom owner-drawn combo box for fonts (like KiCad FONT_CHOICE)
class FontComboBox : public wxOwnerDrawnComboBox
{
public:
FontComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
wxSize(200, -1), 0, nullptr, wxCB_READONLY)
{
// Add sample fonts
Append("Default");
Append("Arial");
Append("Times New Roman");
Append("Courier New");
Append("Verdana");
Append("Georgia");
Append("Comic Sans MS");
Append("Impact");
SetSelection(0);
}
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
{
if (item == wxNOT_FOUND)
return;
// Draw background
if (flags & wxODCB_PAINTING_SELECTED)
{
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rect);
dc.SetTextForeground(*wxWHITE);
}
else
{
dc.SetTextForeground(*wxBLACK);
}
// Draw text in the font itself (if not Default)
wxString fontName = GetString(item);
wxFont font = dc.GetFont();
if (fontName != "Default")
{
font.SetFaceName(fontName);
}
dc.SetFont(font);
dc.DrawText(fontName, rect.x + 6, rect.y + (rect.height - dc.GetCharHeight()) / 2);
}
virtual wxCoord OnMeasureItem(size_t item) const override
{
return 26; // Slightly taller for fonts
}
};
// Custom owner-drawn combo box with icons (for footprints)
class IconComboBox : public wxOwnerDrawnComboBox
{
public:
IconComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
wxSize(250, -1), 0, nullptr, wxCB_READONLY)
{
// Add items with different icon types
Append("Resistor"); m_types.push_back(0); // Rectangle
Append("Capacitor"); m_types.push_back(1); // Two lines
Append("Inductor"); m_types.push_back(2); // Coil
Append("Diode"); m_types.push_back(3); // Triangle
Append("Transistor"); m_types.push_back(4); // Complex
Append("IC Package"); m_types.push_back(5); // Square with pins
Append("Connector"); m_types.push_back(6); // Dots
SetSelection(0);
}
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
{
if (item == wxNOT_FOUND)
return;
// Draw background
if (flags & wxODCB_PAINTING_SELECTED)
{
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rect);
dc.SetTextForeground(*wxWHITE);
}
else
{
dc.SetTextForeground(*wxBLACK);
}
// Draw icon based on type
int iconX = rect.x + 4;
int iconY = rect.y + 4;
int iconSize = rect.height - 8;
dc.SetPen(wxPen(*wxBLACK, 2));
dc.SetBrush(*wxWHITE_BRUSH);
int type = m_types[item];
switch (type)
{
case 0: // Resistor - zigzag
dc.DrawLine(iconX, iconY + iconSize/2, iconX + iconSize, iconY + iconSize/2);
break;
case 1: // Capacitor - two lines
dc.DrawLine(iconX + iconSize/3, iconY + 2, iconX + iconSize/3, iconY + iconSize - 2);
dc.DrawLine(iconX + 2*iconSize/3, iconY + 2, iconX + 2*iconSize/3, iconY + iconSize - 2);
break;
case 2: // Inductor - coil (3 bumps)
dc.DrawArc(iconX + 4, iconY + iconSize/2, iconX + 10, iconY + iconSize/2, iconX + 7, iconY + iconSize/2 - 4);
break;
case 3: // Diode - triangle
{
wxPoint pts[3] = {
wxPoint(iconX + 2, iconY + 2),
wxPoint(iconX + 2, iconY + iconSize - 2),
wxPoint(iconX + iconSize - 2, iconY + iconSize/2)
};
dc.DrawPolygon(3, pts);
}
break;
case 4: // Transistor - circle with lines
dc.DrawCircle(iconX + iconSize/2, iconY + iconSize/2, iconSize/3);
break;
case 5: // IC - square
dc.DrawRectangle(iconX + 2, iconY + 2, iconSize - 4, iconSize - 4);
break;
case 6: // Connector - dots
dc.SetBrush(*wxBLACK_BRUSH);
dc.DrawCircle(iconX + iconSize/4, iconY + iconSize/2, 3);
dc.DrawCircle(iconX + 3*iconSize/4, iconY + iconSize/2, 3);
break;
}
// Draw text
wxString text = GetString(item);
dc.DrawText(text, rect.x + iconSize + 10, rect.y + (rect.height - dc.GetCharHeight()) / 2);
}
virtual wxCoord OnMeasureItem(size_t item) const override
{
return 28;
}
private:
std::vector<int> m_types;
};
class OwnerDrawnFrame : public wxFrame
{
public:
OwnerDrawnFrame() : wxFrame(nullptr, wxID_ANY, "wxOwnerDrawnComboBox Test",
wxDefaultPosition, wxSize(700, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxOwnerDrawnComboBox for layer selectors and font choosers.\n"
"Tests: Custom item drawing, variable heights, icons + text.");
mainSizer->Add(desc, 0, wxALL, 5);
// Layer selector
wxStaticBoxSizer* layerSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Layer Selector (Color Swatches)");
m_layerCombo = new LayerComboBox(mainPanel);
m_layerCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnLayerChanged, this);
layerSizer->Add(m_layerCombo, 0, wxALL, 5);
m_layerLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: F.Cu");
layerSizer->Add(m_layerLabel, 0, wxALL, 5);
mainSizer->Add(layerSizer, 0, wxEXPAND | wxALL, 5);
// Font selector
wxStaticBoxSizer* fontSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Font Selector (Font Preview)");
m_fontCombo = new FontComboBox(mainPanel);
m_fontCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnFontChanged, this);
fontSizer->Add(m_fontCombo, 0, wxALL, 5);
m_fontLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: Default");
fontSizer->Add(m_fontLabel, 0, wxALL, 5);
mainSizer->Add(fontSizer, 0, wxEXPAND | wxALL, 5);
// Icon selector
wxStaticBoxSizer* iconSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Component Selector (Icons)");
m_iconCombo = new IconComboBox(mainPanel);
m_iconCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnIconChanged, this);
iconSizer->Add(m_iconCombo, 0, wxALL, 5);
m_iconLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: Resistor");
iconSizer->Add(m_iconLabel, 0, wxALL, 5);
mainSizer->Add(iconSizer, 0, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Owner-drawn combo box test app started");
Log("Owner-drawn combo box test app started");
Log("Each dropdown shows custom-rendered items");
}
private:
void OnLayerChanged(wxCommandEvent& event)
{
int sel = m_layerCombo->GetSelection();
wxString layer = sel != wxNOT_FOUND ? m_layerCombo->GetString(sel) : "";
m_layerLabel->SetLabel("Selected: " + layer);
Log("Layer changed to: " + layer);
}
void OnFontChanged(wxCommandEvent& event)
{
int sel = m_fontCombo->GetSelection();
wxString font = sel != wxNOT_FOUND ? m_fontCombo->GetString(sel) : "";
m_fontLabel->SetLabel("Selected: " + font);
Log("Font changed to: " + font);
}
void OnIconChanged(wxCommandEvent& event)
{
int sel = m_iconCombo->GetSelection();
wxString component = sel != wxNOT_FOUND ? m_iconCombo->GetString(sel) : "";
m_iconLabel->SetLabel("Selected: " + component);
Log("Component changed to: " + component);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
LayerComboBox* m_layerCombo;
FontComboBox* m_fontCombo;
IconComboBox* m_iconCombo;
wxStaticText* m_layerLabel;
wxStaticText* m_fontLabel;
wxStaticText* m_iconLabel;
wxTextCtrl* m_log;
};
class OwnerDrawnApp : public wxApp
{
public:
virtual bool OnInit() override
{
OwnerDrawnFrame* frame = new OwnerDrawnFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(OwnerDrawnApp);

View file

@ -0,0 +1,205 @@
// wxColourPickerCtrl/wxFontPickerCtrl Test - Tests picker controls in WASM
// KiCad uses these for color preferences, layer colors, and font selection
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/clrpicker.h"
#include "wx/fontpicker.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class PickersTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class PickersTestFrame : public wxFrame
{
public:
PickersTestFrame();
private:
wxColourPickerCtrl* m_colourPicker1;
wxColourPickerCtrl* m_colourPicker2;
wxColourPickerCtrl* m_colourPicker3;
wxFontPickerCtrl* m_fontPicker;
wxPanel* m_previewPanel;
wxStaticText* m_fontPreview;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void UpdatePreview();
void OnColourChanged(wxColourPickerEvent& evt);
void OnFontChanged(wxFontPickerEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_COLOUR_PICKER_1 = wxID_HIGHEST + 1,
ID_COLOUR_PICKER_2,
ID_COLOUR_PICKER_3,
ID_FONT_PICKER
};
wxBEGIN_EVENT_TABLE(PickersTestFrame, wxFrame)
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_1, PickersTestFrame::OnColourChanged)
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_2, PickersTestFrame::OnColourChanged)
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_3, PickersTestFrame::OnColourChanged)
EVT_FONTPICKER_CHANGED(ID_FONT_PICKER, PickersTestFrame::OnFontChanged)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(PickersTestApp);
bool PickersTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
PickersTestFrame* frame = new PickersTestFrame();
frame->Show(true);
return true;
}
PickersTestFrame::PickersTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxPicker Controls WASM Test",
wxDefaultPosition, wxSize(700, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"Picker Controls Test\n\n"
"KiCad uses wxColourPickerCtrl for layer colors and color preferences.\n"
"wxFontPickerCtrl is used for text/label font settings.");
mainSizer->Add(desc, 0, wxALL, 10);
// Color pickers section (KiCad layer colors)
wxStaticBoxSizer* colourBox = new wxStaticBoxSizer(wxVERTICAL, this, "Layer Colors (wxColourPickerCtrl)");
wxFlexGridSizer* colourGrid = new wxFlexGridSizer(3, 3, 10, 20);
colourGrid->AddGrowableCol(1);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Front Copper (F.Cu):"), 0, wxALIGN_CENTER_VERTICAL);
m_colourPicker1 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_1, wxColour(255, 0, 0));
colourGrid->Add(m_colourPicker1, 0, wxEXPAND);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#FF0000"), 0, wxALIGN_CENTER_VERTICAL);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Back Copper (B.Cu):"), 0, wxALIGN_CENTER_VERTICAL);
m_colourPicker2 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_2, wxColour(0, 0, 255));
colourGrid->Add(m_colourPicker2, 0, wxEXPAND);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#0000FF"), 0, wxALIGN_CENTER_VERTICAL);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Silkscreen (F.SilkS):"), 0, wxALIGN_CENTER_VERTICAL);
m_colourPicker3 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_3, wxColour(255, 255, 255));
colourGrid->Add(m_colourPicker3, 0, wxEXPAND);
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#FFFFFF"), 0, wxALIGN_CENTER_VERTICAL);
colourBox->Add(colourGrid, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(colourBox, 0, wxEXPAND | wxALL, 10);
// Color preview panel
wxStaticBoxSizer* previewBox = new wxStaticBoxSizer(wxVERTICAL, this, "Color Preview");
m_previewPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 60));
m_previewPanel->SetBackgroundColour(wxColour(50, 50, 50));
previewBox->Add(m_previewPanel, 0, wxEXPAND | wxALL, 10);
mainSizer->Add(previewBox, 0, wxEXPAND | wxALL, 10);
// Font picker section
wxStaticBoxSizer* fontBox = new wxStaticBoxSizer(wxVERTICAL, this, "Text Font (wxFontPickerCtrl)");
wxBoxSizer* fontRow = new wxBoxSizer(wxHORIZONTAL);
fontRow->Add(new wxStaticText(this, wxID_ANY, "Schematic Text Font:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
m_fontPicker = new wxFontPickerCtrl(this, ID_FONT_PICKER,
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL),
wxDefaultPosition, wxDefaultSize, wxFNTP_DEFAULT_STYLE);
fontRow->Add(m_fontPicker, 1, wxEXPAND);
fontBox->Add(fontRow, 0, wxEXPAND | wxALL, 10);
// Font preview
m_fontPreview = new wxStaticText(this, wxID_ANY, "Sample Text: KiCad WASM Port - R1 10k VCC GND");
m_fontPreview->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
fontBox->Add(m_fontPreview, 0, wxALL, 10);
mainSizer->Add(fontBox, 0, 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 - Picker controls test");
UpdatePreview();
LogEvent("Picker controls test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PICKERS_TEST] wxPicker controls test app started successfully');
});
#endif
}
void PickersTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PICKERS_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void PickersTestFrame::UpdatePreview()
{
// Update preview panel with gradient of selected colors
// For simplicity, just use the first color as background
if (m_previewPanel && m_colourPicker1) {
m_previewPanel->SetBackgroundColour(m_colourPicker1->GetColour());
m_previewPanel->Refresh();
}
}
void PickersTestFrame::OnColourChanged(wxColourPickerEvent& evt)
{
wxColour col = evt.GetColour();
wxString hexColor = wxString::Format("#%02X%02X%02X", col.Red(), col.Green(), col.Blue());
wxString pickerName;
switch (evt.GetId()) {
case ID_COLOUR_PICKER_1: pickerName = "Front Copper"; break;
case ID_COLOUR_PICKER_2: pickerName = "Back Copper"; break;
case ID_COLOUR_PICKER_3: pickerName = "Silkscreen"; break;
default: pickerName = "Unknown"; break;
}
LogEvent(wxString::Format("Color changed: %s = %s", pickerName, hexColor));
UpdatePreview();
}
void PickersTestFrame::OnFontChanged(wxFontPickerEvent& evt)
{
wxFont font = evt.GetFont();
m_fontPreview->SetFont(font);
m_fontPreview->Refresh();
LogEvent(wxString::Format("Font changed: %s, %dpt, %s",
font.GetFaceName(),
font.GetPointSize(),
font.GetWeight() == wxFONTWEIGHT_BOLD ? "Bold" : "Normal"));
}

View file

@ -0,0 +1,364 @@
// wxPopupWindow Test - Transient popups like KiCad's toolbar palettes and status popups
// Tests: wxPopupWindow, wxPopupTransientWindow, positioning, auto-dismiss
#include "wx/wx.h"
#include "wx/popupwin.h"
// Simple popup window (like KiCad STATUS_POPUP)
class StatusPopup : public wxPopupWindow
{
public:
StatusPopup(wxWindow* parent, const wxString& message)
: wxPopupWindow(parent, wxBORDER_SIMPLE)
{
wxPanel* panel = new wxPanel(this);
panel->SetBackgroundColour(wxColour(255, 255, 200)); // Light yellow
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* text = new wxStaticText(panel, wxID_ANY, message);
text->SetFont(text->GetFont().Bold());
sizer->Add(text, 0, wxALL, 8);
panel->SetSizer(sizer);
sizer->Fit(panel);
SetClientSize(panel->GetSize());
}
};
// Transient popup with buttons (like KiCad ACTION_TOOLBAR_PALETTE)
class ToolPalettePopup : public wxPopupTransientWindow
{
public:
ToolPalettePopup(wxWindow* parent, wxTextCtrl* log)
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE), m_log(log)
{
wxPanel* panel = new wxPanel(this);
panel->SetBackgroundColour(wxColour(240, 240, 240));
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* title = new wxStaticText(panel, wxID_ANY, "Tool Palette");
title->SetFont(title->GetFont().Bold());
mainSizer->Add(title, 0, wxALL, 5);
// Tool buttons
wxGridSizer* buttonGrid = new wxGridSizer(3, 3, 2, 2);
for (int i = 1; i <= 9; i++)
{
wxButton* btn = new wxButton(panel, 1000 + i,
wxString::Format("T%d", i),
wxDefaultPosition, wxSize(40, 40));
btn->Bind(wxEVT_BUTTON, &ToolPalettePopup::OnToolClick, this);
buttonGrid->Add(btn, 0);
}
mainSizer->Add(buttonGrid, 0, wxALL, 5);
panel->SetSizer(mainSizer);
mainSizer->Fit(panel);
SetClientSize(panel->GetSize());
}
private:
void OnToolClick(wxCommandEvent& event)
{
int toolNum = event.GetId() - 1000;
m_log->AppendText(wxString::Format("Tool %d clicked\n", toolNum));
Dismiss(); // Close popup after selection
}
wxTextCtrl* m_log;
};
// Color picker popup (like KiCad color pickers)
class ColorPickerPopup : public wxPopupTransientWindow
{
public:
ColorPickerPopup(wxWindow* parent, wxTextCtrl* log, wxPanel* swatch)
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE), m_log(log), m_swatch(swatch)
{
wxPanel* panel = new wxPanel(this);
panel->SetBackgroundColour(*wxWHITE);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* title = new wxStaticText(panel, wxID_ANY, "Select Color");
mainSizer->Add(title, 0, wxALL, 5);
// Color grid
wxGridSizer* colorGrid = new wxGridSizer(4, 4, 2, 2);
wxColour colors[] = {
*wxRED, wxColour(255, 128, 0), *wxYELLOW, wxColour(128, 255, 0),
*wxGREEN, wxColour(0, 255, 128), *wxCYAN, wxColour(0, 128, 255),
*wxBLUE, wxColour(128, 0, 255), wxColour(255, 0, 255), wxColour(255, 0, 128),
*wxBLACK, wxColour(64, 64, 64), wxColour(128, 128, 128), *wxWHITE
};
for (int i = 0; i < 16; i++)
{
wxPanel* colorBtn = new wxPanel(panel, 2000 + i, wxDefaultPosition, wxSize(30, 30));
colorBtn->SetBackgroundColour(colors[i]);
colorBtn->Bind(wxEVT_LEFT_DOWN, &ColorPickerPopup::OnColorClick, this);
colorGrid->Add(colorBtn, 0);
}
mainSizer->Add(colorGrid, 0, wxALL, 5);
panel->SetSizer(mainSizer);
mainSizer->Fit(panel);
SetClientSize(panel->GetSize());
// Store colors for lookup
for (int i = 0; i < 16; i++)
m_colors[i] = colors[i];
}
private:
void OnColorClick(wxMouseEvent& event)
{
wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject());
if (panel)
{
int colorIdx = panel->GetId() - 2000;
wxColour color = m_colors[colorIdx];
m_swatch->SetBackgroundColour(color);
m_swatch->Refresh();
m_log->AppendText(wxString::Format("Color selected: RGB(%d,%d,%d)\n",
color.Red(), color.Green(), color.Blue()));
Dismiss();
}
}
wxTextCtrl* m_log;
wxPanel* m_swatch;
wxColour m_colors[16];
};
class PopupFrame : public wxFrame
{
public:
PopupFrame() : wxFrame(nullptr, wxID_ANY, "wxPopupWindow Test",
wxDefaultPosition, wxSize(700, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses popup windows for toolbar palettes and status messages.\n"
"Tests: wxPopupWindow, wxPopupTransientWindow, positioning, dismiss.");
mainSizer->Add(desc, 0, wxALL, 5);
// Status popup section
wxStaticBoxSizer* statusSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Status Popup (stays until dismissed)");
m_showStatusBtn = new wxButton(mainPanel, wxID_ANY, "Show Status Popup");
m_showStatusBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowStatusPopup, this);
statusSizer->Add(m_showStatusBtn, 0, wxALL, 5);
m_hideStatusBtn = new wxButton(mainPanel, wxID_ANY, "Hide Status Popup");
m_hideStatusBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnHideStatusPopup, this);
m_hideStatusBtn->Enable(false);
statusSizer->Add(m_hideStatusBtn, 0, wxALL, 5);
mainSizer->Add(statusSizer, 0, wxEXPAND | wxALL, 5);
// Tool palette section
wxStaticBoxSizer* paletteSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Tool Palette (transient - click outside to dismiss)");
wxButton* showPaletteBtn = new wxButton(mainPanel, wxID_ANY, "Show Tool Palette");
showPaletteBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowToolPalette, this);
paletteSizer->Add(showPaletteBtn, 0, wxALL, 5);
mainSizer->Add(paletteSizer, 0, wxEXPAND | wxALL, 5);
// Color picker section
wxStaticBoxSizer* colorSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Color Picker (transient popup)");
m_colorSwatch = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(40, 40));
m_colorSwatch->SetBackgroundColour(*wxRED);
colorSizer->Add(m_colorSwatch, 0, wxALL, 5);
wxButton* showColorBtn = new wxButton(mainPanel, wxID_ANY, "Pick Color...");
showColorBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowColorPicker, this);
colorSizer->Add(showColorBtn, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
mainSizer->Add(colorSizer, 0, wxEXPAND | wxALL, 5);
// Positioning section
wxStaticBoxSizer* posSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Popup Positioning");
wxButton* posAboveBtn = new wxButton(mainPanel, wxID_ANY, "Popup Above Me");
posAboveBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupAbove, this);
posSizer->Add(posAboveBtn, 0, wxALL, 5);
wxButton* posBelowBtn = new wxButton(mainPanel, wxID_ANY, "Popup Below Me");
posBelowBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupBelow, this);
posSizer->Add(posBelowBtn, 0, wxALL, 5);
wxButton* posRightBtn = new wxButton(mainPanel, wxID_ANY, "Popup Right of Me");
posRightBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupRight, this);
posSizer->Add(posRightBtn, 0, wxALL, 5);
mainSizer->Add(posSizer, 0, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Popup window test app started");
Log("Popup window test app started");
}
~PopupFrame()
{
if (m_statusPopup)
{
m_statusPopup->Destroy();
m_statusPopup = nullptr;
}
}
private:
void OnShowStatusPopup(wxCommandEvent& event)
{
if (!m_statusPopup)
{
m_statusPopup = new StatusPopup(this, "Status: Processing...\nPlease wait");
wxPoint pos = m_showStatusBtn->GetScreenPosition();
pos.y += m_showStatusBtn->GetSize().y + 5;
m_statusPopup->SetPosition(pos);
m_statusPopup->Show();
m_hideStatusBtn->Enable(true);
Log("Status popup shown");
}
}
void OnHideStatusPopup(wxCommandEvent& event)
{
if (m_statusPopup)
{
m_statusPopup->Hide();
m_statusPopup->Destroy();
m_statusPopup = nullptr;
m_hideStatusBtn->Enable(false);
Log("Status popup hidden");
}
}
void OnShowToolPalette(wxCommandEvent& event)
{
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
ToolPalettePopup* popup = new ToolPalettePopup(this, m_log);
wxPoint pos = btn->GetScreenPosition();
pos.y += btn->GetSize().y + 5;
popup->SetPosition(pos);
popup->Popup();
Log("Tool palette shown (click outside to dismiss)");
}
void OnShowColorPicker(wxCommandEvent& event)
{
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
ColorPickerPopup* popup = new ColorPickerPopup(this, m_log, m_colorSwatch);
wxPoint pos = btn->GetScreenPosition();
pos.y += btn->GetSize().y + 5;
popup->SetPosition(pos);
popup->Popup();
Log("Color picker shown");
}
void OnPopupAbove(wxCommandEvent& event)
{
ShowPositionedPopup(event, 0, -1);
}
void OnPopupBelow(wxCommandEvent& event)
{
ShowPositionedPopup(event, 0, 1);
}
void OnPopupRight(wxCommandEvent& event)
{
ShowPositionedPopup(event, 1, 0);
}
void ShowPositionedPopup(wxCommandEvent& event, int xDir, int yDir)
{
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
class SimpleTransientPopup : public wxPopupTransientWindow
{
public:
SimpleTransientPopup(wxWindow* parent, const wxString& msg)
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE)
{
wxPanel* panel = new wxPanel(this);
panel->SetBackgroundColour(wxColour(200, 220, 255));
wxStaticText* text = new wxStaticText(panel, wxID_ANY, msg);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(text, 0, wxALL, 10);
panel->SetSizer(sizer);
sizer->Fit(panel);
SetClientSize(panel->GetSize());
}
};
wxString direction;
if (yDir < 0) direction = "Above";
else if (yDir > 0) direction = "Below";
else if (xDir > 0) direction = "Right";
SimpleTransientPopup* popup = new SimpleTransientPopup(this, "Popup " + direction + "!");
wxPoint pos = btn->GetScreenPosition();
wxSize btnSize = btn->GetSize();
wxSize popupSize = popup->GetSize();
if (yDir < 0)
pos.y -= popupSize.y + 5;
else if (yDir > 0)
pos.y += btnSize.y + 5;
if (xDir > 0)
pos.x += btnSize.x + 5;
popup->SetPosition(pos);
popup->Popup();
Log("Positioned popup shown " + direction.Lower());
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxButton* m_showStatusBtn;
wxButton* m_hideStatusBtn;
wxPanel* m_colorSwatch;
wxTextCtrl* m_log;
StatusPopup* m_statusPopup = nullptr;
};
class PopupApp : public wxApp
{
public:
virtual bool OnInit() override
{
PopupFrame* frame = new PopupFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(PopupApp);

View file

@ -0,0 +1,496 @@
// wxPrinting Test - Tests print functionality in WASM
// KiCad uses printing for schematic/PCB output
//
// Tests:
// - wxPrintout callbacks (OnPrintPage, OnBeginPrinting, etc.)
// - wxPrintPreview rendering
// - wxPrinter::Print() triggering
// - window.print() browser integration
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#if wxUSE_PRINTING_ARCHITECTURE
#include "wx/print.h"
#include "wx/printdlg.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
// Forward declarations
class PrintTestApp;
class PrintTestFrame;
class TestPrintout;
// Global print data
static wxPrintData* g_printData = nullptr;
static wxPageSetupDialogData* g_pageSetupData = nullptr;
// ============================================================
// TestPrintout - Simple printable document (2 pages)
// ============================================================
class TestPrintout : public wxPrintout
{
public:
TestPrintout(const wxString& title = "Test Printout")
: wxPrintout(title) {}
bool OnPrintPage(int page) override;
bool HasPage(int page) override { return page >= 1 && page <= 2; }
void GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo) override;
bool OnBeginDocument(int startPage, int endPage) override;
void OnEndDocument() override;
void OnBeginPrinting() override;
void OnEndPrinting() override;
private:
void DrawPageOne(wxDC* dc);
void DrawPageTwo(wxDC* dc);
void LogEvent(const wxString& msg);
};
// ============================================================
// PrintTestFrame - Main test window
// ============================================================
class PrintTestFrame : public wxFrame
{
public:
PrintTestFrame();
~PrintTestFrame();
private:
wxTextCtrl* m_log;
wxPanel* m_previewPanel;
void LogEvent(const wxString& msg);
void DrawDocument(wxDC& dc);
// Event handlers
void OnPrintPreview(wxCommandEvent& evt);
void OnPrint(wxCommandEvent& evt);
void OnBrowserPrint(wxCommandEvent& evt);
void OnPageSetup(wxCommandEvent& evt);
void OnPreviewPanelPaint(wxPaintEvent& evt);
wxDECLARE_EVENT_TABLE();
};
// ============================================================
// PrintTestApp
// ============================================================
class PrintTestApp : public wxApp
{
public:
bool OnInit() override;
int OnExit() override;
};
// IDs
enum {
ID_PRINT_PREVIEW = wxID_HIGHEST + 1,
ID_PRINT,
ID_BROWSER_PRINT,
ID_PAGE_SETUP,
ID_PREVIEW_PANEL
};
wxBEGIN_EVENT_TABLE(PrintTestFrame, wxFrame)
EVT_BUTTON(ID_PRINT_PREVIEW, PrintTestFrame::OnPrintPreview)
EVT_BUTTON(ID_PRINT, PrintTestFrame::OnPrint)
EVT_BUTTON(ID_BROWSER_PRINT, PrintTestFrame::OnBrowserPrint)
EVT_BUTTON(ID_PAGE_SETUP, PrintTestFrame::OnPageSetup)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(PrintTestApp);
// ============================================================
// App Implementation
// ============================================================
bool PrintTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
// Initialize print data
g_printData = new wxPrintData;
g_pageSetupData = new wxPageSetupDialogData;
(*g_pageSetupData) = *g_printData;
g_pageSetupData->SetMarginTopLeft(wxPoint(15, 15));
g_pageSetupData->SetMarginBottomRight(wxPoint(15, 15));
PrintTestFrame* frame = new PrintTestFrame();
frame->Show(true);
return true;
}
int PrintTestApp::OnExit()
{
delete g_printData;
delete g_pageSetupData;
g_printData = nullptr;
g_pageSetupData = nullptr;
return wxApp::OnExit();
}
// ============================================================
// Frame Implementation
// ============================================================
PrintTestFrame::PrintTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxPrinting WASM Test",
wxDefaultPosition, wxSize(800, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxPrinting Test\n\n"
"KiCad uses wxPrinting for schematic and PCB printing.\n"
"Test print preview, print dialog, and browser print integration.");
mainSizer->Add(desc, 0, wxALL, 10);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_PRINT_PREVIEW, "Print Preview"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_PRINT, "Print..."), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_BROWSER_PRINT, "Browser Print"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_PAGE_SETUP, "Page Setup"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 5);
// Preview panel - shows what will be printed
wxStaticBoxSizer* previewBox = new wxStaticBoxSizer(wxVERTICAL, this, "Document Preview");
m_previewPanel = new wxPanel(this, ID_PREVIEW_PANEL, wxDefaultPosition, wxSize(-1, 200));
m_previewPanel->SetBackgroundColour(*wxWHITE);
m_previewPanel->Bind(wxEVT_PAINT, &PrintTestFrame::OnPreviewPanelPaint, this);
previewBox->Add(m_previewPanel, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(previewBox, 1, wxEXPAND | wxALL, 10);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
LogEvent("Print test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PRINT_TEST] wxPrinting test app started successfully');
});
#endif
}
PrintTestFrame::~PrintTestFrame()
{
}
void PrintTestFrame::LogEvent(const wxString& msg)
{
if (m_log)
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PRINT_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void PrintTestFrame::DrawDocument(wxDC& dc)
{
// Draw sample content that will be printed
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
dc.SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.DrawText("Print Test Document - Page 1", 20, 20);
dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc.DrawText("This is a test document for WASM printing.", 20, 50);
// Draw shapes
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
dc.DrawRectangle(20, 80, 150, 80);
dc.DrawText("Rectangle", 60, 110);
dc.SetBrush(*wxCYAN_BRUSH);
dc.DrawCircle(280, 120, 40);
dc.DrawText("Circle", 260, 115);
dc.SetPen(wxPen(*wxRED, 2));
dc.DrawLine(20, 180, 350, 180);
dc.DrawText("Red Line", 160, 185);
}
void PrintTestFrame::OnPreviewPanelPaint(wxPaintEvent& WXUNUSED(evt))
{
wxPaintDC dc(m_previewPanel);
DrawDocument(dc);
}
void PrintTestFrame::OnPrintPreview(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Print Preview...");
wxPrintDialogData printDialogData(*g_printData);
// Create two printouts: one for preview, one for printing from preview
wxPrintPreview* preview = new wxPrintPreview(
new TestPrintout("Preview"),
new TestPrintout("Print"),
&printDialogData
);
if (!preview->IsOk())
{
delete preview;
LogEvent("ERROR: Print preview initialization failed");
wxMessageBox("Print preview failed to initialize.",
"Print Preview Error", wxOK | wxICON_ERROR, this);
return;
}
wxPreviewFrame* frame = new wxPreviewFrame(
preview, this, "Print Preview"
);
frame->InitializeWithModality(wxPreviewFrame_NonModal);
frame->Centre(wxBOTH);
frame->Show();
LogEvent("Print Preview frame shown");
}
void PrintTestFrame::OnPrint(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Print dialog...");
wxPrintDialogData printDialogData(*g_printData);
wxPrinter printer(&printDialogData);
TestPrintout printout("Test Print");
if (!printer.Print(this, &printout, true))
{
if (wxPrinter::GetLastError() == wxPRINTER_ERROR)
{
LogEvent("ERROR: Printing failed - printer error");
wxMessageBox("Printing failed. Printer may not be configured correctly.",
"Print Error", wxOK | wxICON_ERROR, this);
}
else
{
LogEvent("Print cancelled by user");
}
}
else
{
(*g_printData) = printer.GetPrintDialogData().GetPrintData();
LogEvent("Print completed successfully");
}
}
void PrintTestFrame::OnBrowserPrint(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Triggering browser print dialog...");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PRINT_EVENT] Calling window.print() for browser printing');
// In WASM, this triggers the browser's native print dialog
// Users can "Save as PDF" from there
window.print();
});
LogEvent("Browser print dialog triggered (window.print called)");
#else
LogEvent("Browser print only available in WASM build");
wxMessageBox("Browser print is only available in WASM builds.",
"Not Available", wxOK | wxICON_INFORMATION, this);
#endif
}
void PrintTestFrame::OnPageSetup(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Opening Page Setup dialog...");
(*g_pageSetupData) = *g_printData;
wxPageSetupDialog pageSetupDialog(this, g_pageSetupData);
if (pageSetupDialog.ShowModal() == wxID_OK)
{
(*g_printData) = pageSetupDialog.GetPageSetupDialogData().GetPrintData();
(*g_pageSetupData) = pageSetupDialog.GetPageSetupDialogData();
LogEvent("Page setup completed");
}
else
{
LogEvent("Page setup cancelled");
}
}
// ============================================================
// TestPrintout Implementation
// ============================================================
void TestPrintout::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PRINTOUT_CALLBACK] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void TestPrintout::OnBeginPrinting()
{
LogEvent("OnBeginPrinting called");
wxPrintout::OnBeginPrinting();
}
void TestPrintout::OnEndPrinting()
{
LogEvent("OnEndPrinting called");
wxPrintout::OnEndPrinting();
}
bool TestPrintout::OnBeginDocument(int startPage, int endPage)
{
LogEvent(wxString::Format("OnBeginDocument: pages %d to %d", startPage, endPage));
return wxPrintout::OnBeginDocument(startPage, endPage);
}
void TestPrintout::OnEndDocument()
{
LogEvent("OnEndDocument called");
wxPrintout::OnEndDocument();
}
void TestPrintout::GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo)
{
*minPage = 1;
*maxPage = 2;
*selPageFrom = 1;
*selPageTo = 2;
LogEvent("GetPageInfo: 2 pages available");
}
bool TestPrintout::OnPrintPage(int page)
{
LogEvent(wxString::Format("OnPrintPage: printing page %d", page));
wxDC* dc = GetDC();
if (!dc)
{
LogEvent("ERROR: No DC available for printing");
return false;
}
if (page == 1)
DrawPageOne(dc);
else if (page == 2)
DrawPageTwo(dc);
else
return false;
// Draw page number
MapScreenSizeToPage();
dc->SetFont(wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawText(wxString::Format("Page %d of 2", page), 10, 10);
LogEvent(wxString::Format("Page %d drawn successfully", page));
return true;
}
void TestPrintout::DrawPageOne(wxDC* dc)
{
LogEvent("DrawPageOne: drawing content");
// Scale to fit page
FitThisSizeToPage(wxSize(400, 300));
dc->SetBackground(*wxWHITE_BRUSH);
dc->SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc->DrawText("WASM Print Test - Page 1", 50, 50);
dc->SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawText("This tests wxPrinting in WebAssembly.", 50, 80);
dc->DrawText("KiCad uses this for schematic/PCB printing.", 50, 100);
// Shapes
dc->SetPen(*wxBLACK_PEN);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->DrawRectangle(50, 130, 150, 80);
dc->DrawText("Rectangle 150x80", 70, 160);
dc->SetBrush(*wxCYAN_BRUSH);
dc->DrawCircle(300, 170, 40);
dc->DrawText("r=40", 285, 165);
dc->SetPen(wxPen(*wxRED, 2));
dc->DrawLine(50, 230, 350, 230);
}
void TestPrintout::DrawPageTwo(wxDC* dc)
{
LogEvent("DrawPageTwo: drawing content");
// Scale to fit page
FitThisSizeToPage(wxSize(400, 300));
dc->SetBackground(*wxWHITE_BRUSH);
dc->SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc->DrawText("WASM Print Test - Page 2", 50, 50);
dc->SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawText("Second page demonstrates multi-page printing.", 50, 80);
// Draw grid pattern
dc->SetPen(*wxBLACK_PEN);
for (int x = 50; x <= 350; x += 30)
{
dc->DrawLine(x, 120, x, 220);
}
for (int y = 120; y <= 220; y += 20)
{
dc->DrawLine(50, y, 350, y);
}
dc->DrawText("Grid Pattern", 170, 230);
// Draw some text
dc->SetFont(wxFont(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawText("Monospace font test: 0123456789", 50, 260);
}
#else // !wxUSE_PRINTING_ARCHITECTURE
// Fallback if printing is not enabled
#include "wx/wx.h"
class PrintTestApp : public wxApp
{
public:
bool OnInit() override
{
wxMessageBox("wxUSE_PRINTING_ARCHITECTURE is not enabled.\n"
"Printing support is not available.",
"Print Test Error", wxOK | wxICON_ERROR);
return false;
}
};
wxIMPLEMENT_APP(PrintTestApp);
#endif // wxUSE_PRINTING_ARCHITECTURE

View file

@ -0,0 +1,324 @@
// wxPrintPreview Test - Print preview system
// Tests wxPreviewFrame, wxPrintout, wxPrintData for KiCad's print functionality
#include "wx/wx.h"
#include "wx/print.h"
#include "wx/printdlg.h"
#include "wx/dcmemory.h"
// Custom Printout class - simulates KiCad schematic/PCB print
class SamplePrintout : public wxPrintout
{
public:
SamplePrintout(const wxString& title = "Sample Printout") : wxPrintout(title) {}
virtual bool OnPrintPage(int page) override
{
wxDC* dc = GetDC();
if (!dc) return false;
// Get page size
int pageW, pageH;
GetPageSizePixels(&pageW, &pageH);
// Draw frame
dc->SetPen(*wxBLACK_PEN);
dc->SetBrush(*wxWHITE_BRUSH);
dc->DrawRectangle(10, 10, pageW - 20, pageH - 20);
// Draw title block (like KiCad)
int titleBlockH = 80;
dc->DrawRectangle(10, pageH - titleBlockH - 10, pageW - 20, titleBlockH);
// Draw grid lines
dc->SetPen(wxPen(*wxLIGHT_GREY, 1, wxPENSTYLE_DOT));
int gridSize = 50;
for (int x = gridSize; x < pageW - gridSize; x += gridSize)
{
dc->DrawLine(x, 10, x, pageH - titleBlockH - 10);
}
for (int y = gridSize; y < pageH - titleBlockH - gridSize; y += gridSize)
{
dc->DrawLine(10, y, pageW - 10, y);
}
// Draw some "components" (circles and rectangles)
dc->SetPen(*wxBLACK_PEN);
dc->SetBrush(*wxRED_BRUSH);
dc->DrawCircle(pageW / 4, pageH / 3, 30);
dc->SetBrush(*wxBLUE_BRUSH);
dc->DrawRectangle(pageW / 2, pageH / 3 - 20, 60, 40);
dc->SetBrush(*wxGREEN_BRUSH);
dc->DrawCircle(3 * pageW / 4, pageH / 3, 25);
// Draw "wires" connecting them
dc->SetPen(wxPen(*wxBLACK, 2));
dc->DrawLine(pageW / 4 + 30, pageH / 3, pageW / 2, pageH / 3);
dc->DrawLine(pageW / 2 + 60, pageH / 3, 3 * pageW / 4 - 25, pageH / 3);
// Title block text
dc->SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc->DrawText("KiCad Print Preview Test", 20, pageH - titleBlockH);
dc->SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawText(wxString::Format("Page %d of 1", page), 20, pageH - titleBlockH + 20);
dc->DrawText("Date: 2025-12-03", 20, pageH - titleBlockH + 35);
dc->DrawText("Rev: 1.0", 20, pageH - titleBlockH + 50);
return true;
}
virtual bool HasPage(int pageNum) override
{
return pageNum == 1;
}
virtual void GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo) override
{
if (minPage) *minPage = 1;
if (maxPage) *maxPage = 1;
if (selPageFrom) *selPageFrom = 1;
if (selPageTo) *selPageTo = 1;
}
};
class PrintPreviewFrame : public wxFrame
{
public:
PrintPreviewFrame() : wxFrame(nullptr, wxID_ANY, "wxPrintPreview Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxPrintout and wxPreviewFrame for print preview.\n"
"Tests: Print preview, page setup dialog, print data persistence.");
mainSizer->Add(desc, 0, wxALL, 5);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* btnPreview = new wxButton(mainPanel, wxID_ANY, "Print Preview");
wxButton* btnPageSetup = new wxButton(mainPanel, wxID_ANY, "Page Setup");
wxButton* btnPrint = new wxButton(mainPanel, wxID_ANY, "Print...");
btnPreview->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPrintPreview, this);
btnPageSetup->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPageSetup, this);
btnPrint->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPrint, this);
btnSizer->Add(btnPreview, 0, wxRIGHT, 5);
btnSizer->Add(btnPageSetup, 0, wxRIGHT, 5);
btnSizer->Add(btnPrint, 0);
mainSizer->Add(btnSizer, 0, wxALL, 5);
// Preview area (draws same content as printout)
m_previewPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxBORDER_SUNKEN);
m_previewPanel->SetBackgroundColour(*wxWHITE);
m_previewPanel->Bind(wxEVT_PAINT, &PrintPreviewFrame::OnPaintPreview, this);
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Preview Area:"), 0, wxLEFT | wxTOP, 5);
mainSizer->Add(m_previewPanel, 1, wxEXPAND | wxALL, 5);
// Settings display
wxStaticBoxSizer* settingsSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Print Settings");
m_settingsText = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition,
wxSize(-1, 80), wxTE_MULTILINE | wxTE_READONLY);
settingsSizer->Add(m_settingsText, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(settingsSizer, 0, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
// Initialize print data
m_printData = new wxPrintData();
m_pageSetupData = new wxPageSetupDialogData(*m_printData);
UpdateSettingsDisplay();
CreateStatusBar();
SetStatusText("Print preview test app started");
Log("Print preview test app started");
}
~PrintPreviewFrame()
{
delete m_printData;
delete m_pageSetupData;
}
private:
void OnPrintPreview(wxCommandEvent& event)
{
Log("Opening print preview...");
// Create two printouts - one for preview, one for printing
SamplePrintout* printoutPreview = new SamplePrintout("Preview");
SamplePrintout* printoutPrint = new SamplePrintout("Print");
wxPrintPreview* preview = new wxPrintPreview(printoutPreview, printoutPrint, m_printData);
if (!preview->IsOk())
{
delete preview;
Log("ERROR: Failed to create print preview");
wxMessageBox("Failed to create print preview", "Error", wxOK | wxICON_ERROR);
return;
}
wxPreviewFrame* frame = new wxPreviewFrame(preview, this, "Print Preview",
wxDefaultPosition, wxSize(700, 500));
frame->Centre();
frame->Initialize();
frame->Show();
Log("Print preview opened successfully");
}
void OnPageSetup(wxCommandEvent& event)
{
Log("Opening page setup dialog...");
*m_pageSetupData = *m_printData;
wxPageSetupDialog pageSetupDialog(this, m_pageSetupData);
if (pageSetupDialog.ShowModal() == wxID_OK)
{
*m_pageSetupData = pageSetupDialog.GetPageSetupDialogData();
*m_printData = m_pageSetupData->GetPrintData();
Log("Page setup changed");
UpdateSettingsDisplay();
}
else
{
Log("Page setup cancelled");
}
}
void OnPrint(wxCommandEvent& event)
{
Log("Opening print dialog...");
wxPrintDialogData printDialogData(*m_printData);
wxPrintDialog printDialog(this, &printDialogData);
if (printDialog.ShowModal() == wxID_OK)
{
*m_printData = printDialog.GetPrintDialogData().GetPrintData();
Log("Print initiated (simulated in browser)");
UpdateSettingsDisplay();
}
else
{
Log("Print cancelled");
}
}
void OnPaintPreview(wxPaintEvent& event)
{
wxPaintDC dc(m_previewPanel);
wxSize size = m_previewPanel->GetClientSize();
// Draw frame
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(5, 5, size.x - 10, size.y - 10);
// Draw title block
int titleBlockH = 40;
dc.DrawRectangle(5, size.y - titleBlockH - 5, size.x - 10, titleBlockH);
// Draw grid
dc.SetPen(wxPen(*wxLIGHT_GREY, 1, wxPENSTYLE_DOT));
int gridSize = 30;
for (int x = gridSize; x < size.x - gridSize; x += gridSize)
{
dc.DrawLine(x, 5, x, size.y - titleBlockH - 5);
}
for (int y = gridSize; y < size.y - titleBlockH - gridSize; y += gridSize)
{
dc.DrawLine(5, y, size.x - 5, y);
}
// Draw components
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxRED_BRUSH);
dc.DrawCircle(size.x / 4, size.y / 3, 15);
dc.SetBrush(*wxBLUE_BRUSH);
dc.DrawRectangle(size.x / 2, size.y / 3 - 10, 30, 20);
dc.SetBrush(*wxGREEN_BRUSH);
dc.DrawCircle(3 * size.x / 4, size.y / 3, 12);
// Wires
dc.SetPen(wxPen(*wxBLACK, 2));
dc.DrawLine(size.x / 4 + 15, size.y / 3, size.x / 2, size.y / 3);
dc.DrawLine(size.x / 2 + 30, size.y / 3, 3 * size.x / 4 - 12, size.y / 3);
// Title block text
dc.SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.DrawText("Sample Schematic", 10, size.y - titleBlockH + 5);
}
void UpdateSettingsDisplay()
{
wxString settings;
settings += wxString::Format("Orientation: %s\n",
m_printData->GetOrientation() == wxLANDSCAPE ? "Landscape" : "Portrait");
settings += wxString::Format("Paper Size: %s\n", GetPaperSizeName(m_printData->GetPaperId()));
settings += wxString::Format("Quality: %d dpi\n", m_printData->GetQuality());
settings += wxString::Format("Colour: %s", m_printData->GetColour() ? "Yes" : "No");
m_settingsText->SetValue(settings);
}
wxString GetPaperSizeName(wxPaperSize paperId)
{
switch (paperId)
{
case wxPAPER_LETTER: return "Letter";
case wxPAPER_A4: return "A4";
case wxPAPER_A3: return "A3";
case wxPAPER_LEGAL: return "Legal";
default: return "Default";
}
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxPanel* m_previewPanel;
wxTextCtrl* m_settingsText;
wxTextCtrl* m_log;
wxPrintData* m_printData;
wxPageSetupDialogData* m_pageSetupData;
};
class PrintPreviewApp : public wxApp
{
public:
virtual bool OnInit() override
{
PrintPreviewFrame* frame = new PrintPreviewFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(PrintPreviewApp);

View file

@ -0,0 +1,246 @@
// wxPropertyGrid Test - Tests property grid in WASM
// KiCad uses property grids for property panels in ALL editors
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/notebook.h"
#include "wx/propgrid/propgrid.h"
#include "wx/propgrid/manager.h"
#include "wx/propgrid/advprops.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class PropGridTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class PropGridTestFrame : public wxFrame
{
public:
PropGridTestFrame();
private:
wxPropertyGrid* m_propGrid;
wxPropertyGridManager* m_propGridManager;
wxTextCtrl* m_log;
wxNotebook* m_notebook;
void LogEvent(const wxString& msg);
void PopulateBasicGrid();
void PopulateManagerGrid();
void OnPropertyChanged(wxPropertyGridEvent& evt);
void OnPropertyChanging(wxPropertyGridEvent& evt);
void OnPropertySelected(wxPropertyGridEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_PROPGRID = wxID_HIGHEST + 1,
ID_PROPGRID_MANAGER,
ID_NOTEBOOK
};
wxBEGIN_EVENT_TABLE(PropGridTestFrame, wxFrame)
EVT_PG_CHANGED(ID_PROPGRID, PropGridTestFrame::OnPropertyChanged)
EVT_PG_CHANGING(ID_PROPGRID, PropGridTestFrame::OnPropertyChanging)
EVT_PG_SELECTED(ID_PROPGRID, PropGridTestFrame::OnPropertySelected)
EVT_PG_CHANGED(ID_PROPGRID_MANAGER, PropGridTestFrame::OnPropertyChanged)
EVT_PG_SELECTED(ID_PROPGRID_MANAGER, PropGridTestFrame::OnPropertySelected)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(PropGridTestApp);
bool PropGridTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
PropGridTestFrame* frame = new PropGridTestFrame();
frame->Show(true);
return true;
}
PropGridTestFrame::PropGridTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxPropertyGrid WASM Test",
wxDefaultPosition, wxSize(800, 700))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxPropertyGrid Test\n\n"
"KiCad uses wxPropertyGrid for property panels in ALL editors.\n"
"This tests property grid rendering and editing.");
mainSizer->Add(desc, 0, wxALL, 10);
// Notebook with two tabs
m_notebook = new wxNotebook(this, ID_NOTEBOOK);
// Tab 1: Basic PropertyGrid
wxPanel* basicPanel = new wxPanel(m_notebook);
wxBoxSizer* basicSizer = new wxBoxSizer(wxVERTICAL);
m_propGrid = new wxPropertyGrid(basicPanel, ID_PROPGRID,
wxDefaultPosition, wxSize(-1, 300),
wxPG_SPLITTER_AUTO_CENTER | wxPG_DEFAULT_STYLE);
basicSizer->Add(m_propGrid, 1, wxEXPAND | wxALL, 5);
basicPanel->SetSizer(basicSizer);
m_notebook->AddPage(basicPanel, "Basic PropertyGrid");
// Tab 2: PropertyGridManager (multi-page)
wxPanel* managerPanel = new wxPanel(m_notebook);
wxBoxSizer* managerSizer = new wxBoxSizer(wxVERTICAL);
m_propGridManager = new wxPropertyGridManager(managerPanel, ID_PROPGRID_MANAGER,
wxDefaultPosition, wxSize(-1, 300),
wxPG_SPLITTER_AUTO_CENTER | wxPGMAN_DEFAULT_STYLE);
managerSizer->Add(m_propGridManager, 1, wxEXPAND | wxALL, 5);
managerPanel->SetSizer(managerSizer);
m_notebook->AddPage(managerPanel, "PropertyGridManager");
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, 120), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
// Populate grids after layout is set
PopulateBasicGrid();
PopulateManagerGrid();
CreateStatusBar();
SetStatusText("Ready - wxPropertyGrid test");
LogEvent("PropertyGrid test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PROPGRID_TEST] wxPropertyGrid test app started successfully');
});
#endif
}
void PropGridTestFrame::PopulateBasicGrid()
{
// KiCad-like properties for a component
m_propGrid->Append(new wxPropertyCategory("General"));
m_propGrid->Append(new wxStringProperty("Reference", wxPG_LABEL, "R1"));
m_propGrid->Append(new wxStringProperty("Value", wxPG_LABEL, "10k"));
m_propGrid->Append(new wxStringProperty("Footprint", wxPG_LABEL, "Resistor_SMD:R_0402"));
m_propGrid->Append(new wxPropertyCategory("Position"));
m_propGrid->Append(new wxFloatProperty("X", wxPG_LABEL, 100.5));
m_propGrid->Append(new wxFloatProperty("Y", wxPG_LABEL, 50.25));
m_propGrid->Append(new wxIntProperty("Rotation", wxPG_LABEL, 90));
m_propGrid->Append(new wxPropertyCategory("Display"));
m_propGrid->Append(new wxBoolProperty("Show Reference", wxPG_LABEL, true));
m_propGrid->Append(new wxBoolProperty("Show Value", wxPG_LABEL, true));
// Color property (KiCad layer colors)
m_propGrid->Append(new wxPropertyCategory("Colors"));
m_propGrid->Append(new wxColourProperty("Front Copper", wxPG_LABEL, wxColour(255, 0, 0)));
m_propGrid->Append(new wxColourProperty("Back Copper", wxPG_LABEL, wxColour(0, 0, 255)));
// Enum property (like KiCad layer selection)
wxPGChoices layerChoices;
layerChoices.Add("F.Cu", 0);
layerChoices.Add("B.Cu", 1);
layerChoices.Add("F.SilkS", 2);
layerChoices.Add("B.SilkS", 3);
layerChoices.Add("Edge.Cuts", 4);
m_propGrid->Append(new wxEnumProperty("Layer", wxPG_LABEL, layerChoices, 0));
LogEvent("Basic PropertyGrid populated with KiCad-like properties");
}
void PropGridTestFrame::PopulateManagerGrid()
{
// Page 1: Component properties
wxPropertyGridPage* page1 = m_propGridManager->AddPage("Component");
page1->Append(new wxPropertyCategory("Identity"));
page1->Append(new wxStringProperty("Reference", wxPG_LABEL, "U1"));
page1->Append(new wxStringProperty("Value", wxPG_LABEL, "STM32F103"));
page1->Append(new wxStringProperty("Library", wxPG_LABEL, "MCU_ST_STM32F1"));
page1->Append(new wxPropertyCategory("Attributes"));
page1->Append(new wxBoolProperty("Exclude from BOM", wxPG_LABEL, false));
page1->Append(new wxBoolProperty("Exclude from Board", wxPG_LABEL, false));
// Page 2: Footprint properties
wxPropertyGridPage* page2 = m_propGridManager->AddPage("Footprint");
page2->Append(new wxPropertyCategory("Footprint"));
page2->Append(new wxStringProperty("Name", wxPG_LABEL, "LQFP-48_7x7mm_P0.5mm"));
page2->Append(new wxIntProperty("Pads", wxPG_LABEL, 48));
page2->Append(new wxPropertyCategory("3D Model"));
page2->Append(new wxStringProperty("3D Model Path", wxPG_LABEL, "${KISYS3DMOD}/Package_QFP.3dshapes/LQFP-48_7x7mm_P0.5mm.wrl"));
page2->Append(new wxFloatProperty("Scale X", wxPG_LABEL, 1.0));
page2->Append(new wxFloatProperty("Scale Y", wxPG_LABEL, 1.0));
page2->Append(new wxFloatProperty("Scale Z", wxPG_LABEL, 1.0));
// Page 3: Net properties
wxPropertyGridPage* page3 = m_propGridManager->AddPage("Net");
page3->Append(new wxPropertyCategory("Net Info"));
page3->Append(new wxStringProperty("Net Name", wxPG_LABEL, "VCC"));
page3->Append(new wxIntProperty("Net Code", wxPG_LABEL, 42));
page3->Append(new wxIntProperty("Connected Pads", wxPG_LABEL, 12));
LogEvent("PropertyGridManager populated with 3 pages");
}
void PropGridTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[PROPGRID_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void PropGridTestFrame::OnPropertyChanged(wxPropertyGridEvent& evt)
{
wxPGProperty* prop = evt.GetProperty();
if (prop) {
LogEvent(wxString::Format("Property changed: '%s' = '%s'",
prop->GetName(), prop->GetValueAsString()));
}
}
void PropGridTestFrame::OnPropertyChanging(wxPropertyGridEvent& evt)
{
wxPGProperty* prop = evt.GetProperty();
if (prop) {
LogEvent(wxString::Format("Property changing: '%s' -> '%s'",
prop->GetName(), evt.GetValue().GetString()));
}
}
void PropGridTestFrame::OnPropertySelected(wxPropertyGridEvent& evt)
{
wxPGProperty* prop = evt.GetProperty();
if (prop) {
LogEvent(wxString::Format("Property selected: '%s'", prop->GetName()));
}
}

View file

@ -0,0 +1,235 @@
// Region Clipping Test - Tests non-rectangular clipping regions
// Tests: wxRegion, wxDC::SetDeviceClippingRegion(), Union/Subtract/Intersect
#include "wx/wx.h"
#include "wx/dcmemory.h"
#include "wx/region.h"
class RegionsFrame : public wxFrame
{
public:
RegionsFrame() : wxFrame(nullptr, wxID_ANY, "Region Clipping Test",
wxDefaultPosition, wxSize(900, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"Tests non-rectangular region clipping.\n"
"Uses wxRegion with Union, Subtract, and Intersect operations.");
mainSizer->Add(desc, 0, wxALL, 5);
// Drawing panel
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 550));
m_drawPanel->SetBackgroundColour(*wxWHITE);
m_drawPanel->Bind(wxEVT_PAINT, &RegionsFrame::OnPaint, this);
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Region clipping test app started");
Log("Region clipping test app started");
Log("Testing non-rectangular region clipping");
}
private:
void DrawTestPattern(wxDC& dc, int x, int y, int w, int h)
{
// Draw a gradient-like pattern that makes clipping visible
for (int i = 0; i < w; i += 5)
{
dc.SetPen(wxPen(wxColour(255 * i / w, 100, 255 - 255 * i / w)));
dc.DrawLine(x + i, y, x + i, y + h);
}
// Draw some shapes
dc.SetBrush(wxBrush(wxColour(255, 200, 0, 128)));
dc.SetPen(wxPen(*wxBLACK, 2));
dc.DrawCircle(x + w/2, y + h/2, w/3);
dc.SetBrush(wxBrush(wxColour(0, 200, 255, 128)));
dc.DrawRectangle(x + w/4, y + h/4, w/2, h/2);
}
void OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(m_drawPanel);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
int y = 10;
const int boxWidth = 200;
const int boxHeight = 150;
const int spacing = 20;
dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
// Row 1: Basic region types
// 1a: No clipping (reference)
dc.DrawText("No Clipping (reference)", 20, y);
y += 15;
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
// 1b: Single rectangle clipping
dc.DrawText("Single Rectangle", 20 + boxWidth + spacing, y - 15);
wxRegion singleRect(20 + boxWidth + spacing + 20, y + 20, boxWidth - 40, boxHeight - 40);
dc.SetDeviceClippingRegion(singleRect);
DrawTestPattern(dc, 20 + boxWidth + spacing, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 1c: Two rectangles (union)
dc.DrawText("Two Rectangles (Union)", 20 + (boxWidth + spacing) * 2, y - 15);
wxRegion twoRects;
twoRects.Union(wxRect(20 + (boxWidth + spacing) * 2, y, boxWidth / 2, boxHeight / 2));
twoRects.Union(wxRect(20 + (boxWidth + spacing) * 2 + boxWidth / 2, y + boxHeight / 2, boxWidth / 2, boxHeight / 2));
dc.SetDeviceClippingRegion(twoRects);
DrawTestPattern(dc, 20 + (boxWidth + spacing) * 2, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 1d: L-shaped region
dc.DrawText("L-Shape (Union)", 20 + (boxWidth + spacing) * 3, y - 15);
wxRegion lShape;
int lx = 20 + (boxWidth + spacing) * 3;
lShape.Union(wxRect(lx, y, boxWidth / 3, boxHeight)); // Vertical bar
lShape.Union(wxRect(lx, y + boxHeight * 2 / 3, boxWidth, boxHeight / 3)); // Horizontal bar
dc.SetDeviceClippingRegion(lShape);
DrawTestPattern(dc, lx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
y += boxHeight + 40;
// Row 2: Complex regions
// 2a: Cross shape
dc.DrawText("Cross (Two Rects)", 20, y);
y += 15;
wxRegion cross;
cross.Union(wxRect(20 + boxWidth / 3, y, boxWidth / 3, boxHeight)); // Vertical
cross.Union(wxRect(20, y + boxHeight / 3, boxWidth, boxHeight / 3)); // Horizontal
dc.SetDeviceClippingRegion(cross);
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 2b: Checkerboard pattern (4 squares)
dc.DrawText("Checkerboard (4 Rects)", 20 + boxWidth + spacing, y - 15);
wxRegion checkerboard;
int cx = 20 + boxWidth + spacing;
int halfW = boxWidth / 2;
int halfH = boxHeight / 2;
checkerboard.Union(wxRect(cx, y, halfW, halfH));
checkerboard.Union(wxRect(cx + halfW, y + halfH, halfW, halfH));
dc.SetDeviceClippingRegion(checkerboard);
DrawTestPattern(dc, cx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 2c: Diagonal stripes (multiple rectangles)
dc.DrawText("Stripes (Multiple)", 20 + (boxWidth + spacing) * 2, y - 15);
wxRegion stripes;
int sx = 20 + (boxWidth + spacing) * 2;
int stripeW = boxWidth / 5;
for (int i = 0; i < 3; i++)
{
stripes.Union(wxRect(sx + i * stripeW * 2, y, stripeW, boxHeight));
}
dc.SetDeviceClippingRegion(stripes);
DrawTestPattern(dc, sx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 2d: Frame (subtract inner rect)
dc.DrawText("Frame (Subtract)", 20 + (boxWidth + spacing) * 3, y - 15);
wxRegion frame(20 + (boxWidth + spacing) * 3, y, boxWidth, boxHeight);
int fx = 20 + (boxWidth + spacing) * 3;
frame.Subtract(wxRect(fx + 30, y + 30, boxWidth - 60, boxHeight - 60));
dc.SetDeviceClippingRegion(frame);
DrawTestPattern(dc, fx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
y += boxHeight + 40;
// Row 3: More complex patterns
// 3a: Grid pattern
dc.DrawText("Grid (9 Rects)", 20, y);
y += 15;
wxRegion grid;
int cellW = boxWidth / 5;
int cellH = boxHeight / 5;
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
grid.Union(wxRect(20 + col * cellW * 2, y + row * cellH * 2, cellW, cellH));
}
}
dc.SetDeviceClippingRegion(grid);
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 3b: T-shape
dc.DrawText("T-Shape", 20 + boxWidth + spacing, y - 15);
wxRegion tShape;
int tx = 20 + boxWidth + spacing;
tShape.Union(wxRect(tx, y, boxWidth, boxHeight / 3)); // Top bar
tShape.Union(wxRect(tx + boxWidth / 3, y, boxWidth / 3, boxHeight)); // Vertical stem
dc.SetDeviceClippingRegion(tShape);
DrawTestPattern(dc, tx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 3c: Corner rectangles
dc.DrawText("4 Corners", 20 + (boxWidth + spacing) * 2, y - 15);
wxRegion corners;
int cornx = 20 + (boxWidth + spacing) * 2;
int cornSize = boxWidth / 4;
corners.Union(wxRect(cornx, y, cornSize, cornSize)); // Top-left
corners.Union(wxRect(cornx + boxWidth - cornSize, y, cornSize, cornSize)); // Top-right
corners.Union(wxRect(cornx, y + boxHeight - cornSize, cornSize, cornSize)); // Bottom-left
corners.Union(wxRect(cornx + boxWidth - cornSize, y + boxHeight - cornSize, cornSize, cornSize)); // Bottom-right
dc.SetDeviceClippingRegion(corners);
DrawTestPattern(dc, cornx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
// 3d: Staircase
dc.DrawText("Staircase", 20 + (boxWidth + spacing) * 3, y - 15);
wxRegion stairs;
int stx = 20 + (boxWidth + spacing) * 3;
int stepW = boxWidth / 4;
int stepH = boxHeight / 4;
for (int i = 0; i < 4; i++)
{
stairs.Union(wxRect(stx + i * stepW, y + i * stepH, boxWidth - i * stepW, stepH));
}
dc.SetDeviceClippingRegion(stairs);
DrawTestPattern(dc, stx, y, boxWidth, boxHeight);
dc.DestroyClippingRegion();
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxPanel* m_drawPanel;
wxTextCtrl* m_log;
};
class RegionsApp : public wxApp
{
public:
virtual bool OnInit() override
{
RegionsFrame* frame = new RegionsFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(RegionsApp);

View file

@ -0,0 +1,278 @@
// Specialized wxWidgets Controls Test
// Tests: wxTreebook, wxBitmapComboBox, wxSpinCtrl
// These are used in KiCad for settings dialogs and layer management
#include "wx/wx.h"
#include "wx/treebook.h"
#include "wx/bmpcbox.h"
#include "wx/dcmemory.h"
#include "wx/spinctrl.h"
// Helper to create color swatch bitmaps for wxBitmapComboBox
wxBitmap CreateColorSwatch(const wxColour& color, int width = 16, int height = 16)
{
wxBitmap bmp(width, height);
wxMemoryDC dc(bmp);
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(wxBrush(color));
dc.DrawRectangle(0, 0, width, height);
return bmp;
}
class SpecializedFrame : public wxFrame
{
public:
SpecializedFrame() : wxFrame(nullptr, wxID_ANY, "Specialized wxWidgets Controls Test",
wxDefaultPosition, wxSize(900, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses specialized controls for settings and layer management.\n"
"Tests: wxTreebook (settings pages), wxBitmapComboBox (layer chooser), wxListBox (layer list).");
mainSizer->Add(desc, 0, wxALL, 5);
// Split into left and right panels
wxBoxSizer* contentSizer = new wxBoxSizer(wxHORIZONTAL);
// Left: wxTreebook
wxStaticBoxSizer* treebookSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "wxTreebook (Settings)");
CreateTreebook(mainPanel);
treebookSizer->Add(m_treebook, 1, wxEXPAND | wxALL, 5);
contentSizer->Add(treebookSizer, 1, wxEXPAND | wxALL, 5);
// Right: wxBitmapComboBox and wxListBox for layer list
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
// wxBitmapComboBox
wxStaticBoxSizer* bmpComboSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "wxBitmapComboBox (Layer Chooser)");
CreateBitmapComboBox(mainPanel);
bmpComboSizer->Add(m_layerCombo, 0, wxEXPAND | wxALL, 5);
wxStaticText* comboLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: (none)");
m_comboLabel = comboLabel;
bmpComboSizer->Add(comboLabel, 0, wxALL, 5);
rightSizer->Add(bmpComboSizer, 0, wxEXPAND | wxALL, 5);
// Layer List (simulating wxRearrangeCtrl with wxCheckListBox)
wxStaticBoxSizer* listSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Layer List (Visibility)");
CreateLayerList(mainPanel);
listSizer->Add(m_layerList, 1, wxEXPAND | wxALL, 5);
wxButton* btnGetOrder = new wxButton(mainPanel, wxID_ANY, "Get Layer Status");
btnGetOrder->Bind(wxEVT_BUTTON, &SpecializedFrame::OnGetOrder, this);
listSizer->Add(btnGetOrder, 0, wxALL, 5);
rightSizer->Add(listSizer, 1, wxEXPAND | wxALL, 5);
contentSizer->Add(rightSizer, 1, wxEXPAND);
mainSizer->Add(contentSizer, 1, wxEXPAND);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Specialized controls test app started");
Log("Specialized controls test app started");
}
private:
void CreateTreebook(wxWindow* parent)
{
m_treebook = new wxTreebook(parent, wxID_ANY);
// General settings page
wxPanel* generalPage = new wxPanel(m_treebook);
wxBoxSizer* generalSizer = new wxBoxSizer(wxVERTICAL);
generalSizer->Add(new wxStaticText(generalPage, wxID_ANY, "General Settings"), 0, wxALL, 10);
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Show grid"), 0, wxALL, 5);
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Auto-save"), 0, wxALL, 5);
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Show welcome dialog"), 0, wxALL, 5);
generalPage->SetSizer(generalSizer);
m_treebook->AddPage(generalPage, "General");
// Display page with sub-pages
wxPanel* displayPage = new wxPanel(m_treebook);
wxBoxSizer* displaySizer = new wxBoxSizer(wxVERTICAL);
displaySizer->Add(new wxStaticText(displayPage, wxID_ANY, "Display Settings"), 0, wxALL, 10);
displaySizer->Add(new wxCheckBox(displayPage, wxID_ANY, "Anti-aliasing"), 0, wxALL, 5);
displayPage->SetSizer(displaySizer);
m_treebook->AddPage(displayPage, "Display");
// Display sub-page: Colors
wxPanel* colorsPage = new wxPanel(m_treebook);
wxBoxSizer* colorsSizer = new wxBoxSizer(wxVERTICAL);
colorsSizer->Add(new wxStaticText(colorsPage, wxID_ANY, "Color Settings"), 0, wxALL, 10);
colorsSizer->Add(new wxStaticText(colorsPage, wxID_ANY, "Background:"), 0, wxLEFT | wxTOP, 5);
wxChoice* bgChoice = new wxChoice(colorsPage, wxID_ANY);
bgChoice->Append("White");
bgChoice->Append("Black");
bgChoice->Append("Gray");
bgChoice->SetSelection(0);
colorsSizer->Add(bgChoice, 0, wxALL, 5);
colorsPage->SetSizer(colorsSizer);
m_treebook->AddSubPage(colorsPage, "Colors");
// Display sub-page: Grid
wxPanel* gridPage = new wxPanel(m_treebook);
wxBoxSizer* gridSizer = new wxBoxSizer(wxVERTICAL);
gridSizer->Add(new wxStaticText(gridPage, wxID_ANY, "Grid Settings"), 0, wxALL, 10);
gridSizer->Add(new wxStaticText(gridPage, wxID_ANY, "Grid size (mm):"), 0, wxLEFT | wxTOP, 5);
gridSizer->Add(new wxSpinCtrl(gridPage, wxID_ANY, "1", wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 1, 100, 1), 0, wxALL, 5);
gridPage->SetSizer(gridSizer);
m_treebook->AddSubPage(gridPage, "Grid");
// Editing page
wxPanel* editPage = new wxPanel(m_treebook);
wxBoxSizer* editSizer = new wxBoxSizer(wxVERTICAL);
editSizer->Add(new wxStaticText(editPage, wxID_ANY, "Editing Settings"), 0, wxALL, 10);
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Magnetic pads"), 0, wxALL, 5);
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Magnetic graphics"), 0, wxALL, 5);
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Allow free pads"), 0, wxALL, 5);
editPage->SetSizer(editSizer);
m_treebook->AddPage(editPage, "Editing");
// Editing sub-page: Defaults
wxPanel* defaultsPage = new wxPanel(m_treebook);
wxBoxSizer* defaultsSizer = new wxBoxSizer(wxVERTICAL);
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Default Values"), 0, wxALL, 10);
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Track width (mm):"), 0, wxLEFT | wxTOP, 5);
defaultsSizer->Add(new wxTextCtrl(defaultsPage, wxID_ANY, "0.25"), 0, wxALL, 5);
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Via size (mm):"), 0, wxLEFT | wxTOP, 5);
defaultsSizer->Add(new wxTextCtrl(defaultsPage, wxID_ANY, "0.8"), 0, wxALL, 5);
defaultsPage->SetSizer(defaultsSizer);
m_treebook->AddSubPage(defaultsPage, "Defaults");
// Printing page
wxPanel* printPage = new wxPanel(m_treebook);
wxBoxSizer* printSizer = new wxBoxSizer(wxVERTICAL);
printSizer->Add(new wxStaticText(printPage, wxID_ANY, "Print Settings"), 0, wxALL, 10);
printSizer->Add(new wxCheckBox(printPage, wxID_ANY, "Print mirrored"), 0, wxALL, 5);
printSizer->Add(new wxCheckBox(printPage, wxID_ANY, "Print in black"), 0, wxALL, 5);
printPage->SetSizer(printSizer);
m_treebook->AddPage(printPage, "Printing");
m_treebook->Bind(wxEVT_TREEBOOK_PAGE_CHANGED, &SpecializedFrame::OnTreebookPageChanged, this);
}
void CreateBitmapComboBox(wxWindow* parent)
{
m_layerCombo = new wxBitmapComboBox(parent, wxID_ANY, "", wxDefaultPosition,
wxSize(200, -1), 0, nullptr, wxCB_READONLY);
// Add layers with color swatches
m_layerCombo->Append("F.Cu (Top Copper)", CreateColorSwatch(*wxRED));
m_layerCombo->Append("B.Cu (Bottom Copper)", CreateColorSwatch(*wxBLUE));
m_layerCombo->Append("F.SilkS (Top Silk)", CreateColorSwatch(*wxYELLOW));
m_layerCombo->Append("B.SilkS (Bottom Silk)", CreateColorSwatch(wxColour(255, 0, 255)));
m_layerCombo->Append("F.Mask (Top Mask)", CreateColorSwatch(wxColour(0, 128, 0)));
m_layerCombo->Append("B.Mask (Bottom Mask)", CreateColorSwatch(wxColour(0, 128, 128)));
m_layerCombo->Append("Edge.Cuts", CreateColorSwatch(*wxWHITE));
m_layerCombo->Append("Dwgs.User", CreateColorSwatch(wxColour(128, 128, 128)));
m_layerCombo->SetSelection(0);
m_layerCombo->Bind(wxEVT_COMBOBOX, &SpecializedFrame::OnLayerComboChanged, this);
}
void CreateLayerList(wxWindow* parent)
{
wxArrayString items;
items.Add("F.Cu");
items.Add("In1.Cu");
items.Add("In2.Cu");
items.Add("B.Cu");
items.Add("F.SilkS");
items.Add("B.SilkS");
items.Add("F.Mask");
items.Add("B.Mask");
m_layerList = new wxCheckListBox(parent, wxID_ANY, wxDefaultPosition,
wxSize(-1, 200), items);
// Check all by default
for (unsigned int i = 0; i < m_layerList->GetCount(); i++)
{
m_layerList->Check(i, true);
}
m_layerList->Bind(wxEVT_CHECKLISTBOX, &SpecializedFrame::OnLayerListCheck, this);
}
void OnTreebookPageChanged(wxBookCtrlEvent& event)
{
int page = event.GetSelection();
wxString pageName = m_treebook->GetPageText(page);
Log(wxString::Format("Treebook page changed to: %s (page %d)", pageName, page));
}
void OnLayerComboChanged(wxCommandEvent& event)
{
int sel = m_layerCombo->GetSelection();
if (sel != wxNOT_FOUND)
{
wxString layer = m_layerCombo->GetString(sel);
m_comboLabel->SetLabel(wxString::Format("Selected: %s", layer));
Log(wxString::Format("Layer selected: %s", layer));
}
}
void OnLayerListCheck(wxCommandEvent& event)
{
int idx = event.GetInt();
bool checked = m_layerList->IsChecked(idx);
wxString layer = m_layerList->GetString(idx);
Log(wxString::Format("Layer %s visibility: %s", layer, checked ? "visible" : "hidden"));
}
void OnGetOrder(wxCommandEvent& event)
{
wxString orderStr = "Layer status:\n";
for (unsigned int i = 0; i < m_layerList->GetCount(); i++)
{
wxString layer = m_layerList->GetString(i);
bool visible = m_layerList->IsChecked(i);
orderStr += wxString::Format(" %d. %s [%s]\n",
(int)(i + 1), layer, visible ? "visible" : "hidden");
}
Log(orderStr);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxTreebook* m_treebook;
wxBitmapComboBox* m_layerCombo;
wxStaticText* m_comboLabel;
wxCheckListBox* m_layerList;
wxTextCtrl* m_log;
};
class SpecializedApp : public wxApp
{
public:
virtual bool OnInit() override
{
SpecializedFrame* frame = new SpecializedFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(SpecializedApp);

View file

@ -0,0 +1,405 @@
// wxStyledTextCtrl Test - Tests Scintilla-based text editor in WASM
// KiCad uses wxStyledTextCtrl for:
// - DRC rules editor
// - Python console
// - Custom script editors
// This is MEDIUM priority for KiCad functionality
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/stc/stc.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class StcTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class StcTestFrame : public wxFrame
{
public:
StcTestFrame();
private:
wxStyledTextCtrl* m_stc;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void SetupPythonLexer();
void SetupDrcLexer();
void SetupPlainText();
// Event handlers
void OnPythonMode(wxCommandEvent& evt);
void OnDrcMode(wxCommandEvent& evt);
void OnPlainMode(wxCommandEvent& evt);
void OnInsertSample(wxCommandEvent& evt);
void OnClearText(wxCommandEvent& evt);
void OnShowLineNumbers(wxCommandEvent& evt);
void OnFoldCode(wxCommandEvent& evt);
// STC events
void OnStcChange(wxStyledTextEvent& evt);
void OnStcCharAdded(wxStyledTextEvent& evt);
void OnStcMarginClick(wxStyledTextEvent& evt);
void OnStcUpdateUI(wxStyledTextEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_STC = wxID_HIGHEST + 1,
ID_PYTHON_MODE,
ID_DRC_MODE,
ID_PLAIN_MODE,
ID_INSERT_SAMPLE,
ID_CLEAR_TEXT,
ID_SHOW_LINENUMS,
ID_FOLD_CODE
};
wxBEGIN_EVENT_TABLE(StcTestFrame, wxFrame)
EVT_BUTTON(ID_PYTHON_MODE, StcTestFrame::OnPythonMode)
EVT_BUTTON(ID_DRC_MODE, StcTestFrame::OnDrcMode)
EVT_BUTTON(ID_PLAIN_MODE, StcTestFrame::OnPlainMode)
EVT_BUTTON(ID_INSERT_SAMPLE, StcTestFrame::OnInsertSample)
EVT_BUTTON(ID_CLEAR_TEXT, StcTestFrame::OnClearText)
EVT_BUTTON(ID_SHOW_LINENUMS, StcTestFrame::OnShowLineNumbers)
EVT_BUTTON(ID_FOLD_CODE, StcTestFrame::OnFoldCode)
EVT_STC_CHANGE(ID_STC, StcTestFrame::OnStcChange)
EVT_STC_CHARADDED(ID_STC, StcTestFrame::OnStcCharAdded)
EVT_STC_MARGINCLICK(ID_STC, StcTestFrame::OnStcMarginClick)
EVT_STC_UPDATEUI(ID_STC, StcTestFrame::OnStcUpdateUI)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(StcTestApp);
bool StcTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
StcTestFrame* frame = new StcTestFrame();
frame->Show(true);
return true;
}
StcTestFrame::StcTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxStyledTextCtrl WASM Test",
wxDefaultPosition, wxSize(800, 700))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxStyledTextCtrl Test\n\n"
"KiCad uses wxSTC for DRC rules editor, Python console, and script editors.\n"
"Test syntax highlighting, line numbers, folding, and basic editing.");
mainSizer->Add(desc, 0, wxALL, 10);
// Button bar
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_PYTHON_MODE, "Python"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_DRC_MODE, "DRC Rules"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_PLAIN_MODE, "Plain"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_INSERT_SAMPLE, "Insert Sample"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_CLEAR_TEXT, "Clear"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_SHOW_LINENUMS, "Line Numbers"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_FOLD_CODE, "Fold All"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
// wxStyledTextCtrl
m_stc = new wxStyledTextCtrl(this, ID_STC, wxDefaultPosition, wxSize(-1, 350));
// Basic styling
wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
m_stc->StyleSetFont(wxSTC_STYLE_DEFAULT, font);
m_stc->StyleClearAll();
// Line numbers margin
m_stc->SetMarginType(0, wxSTC_MARGIN_NUMBER);
m_stc->SetMarginWidth(0, 40);
// Folding margin
m_stc->SetMarginType(1, wxSTC_MARGIN_SYMBOL);
m_stc->SetMarginMask(1, wxSTC_MASK_FOLDERS);
m_stc->SetMarginWidth(1, 16);
m_stc->SetMarginSensitive(1, true);
// Folding markers
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE);
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER);
// Enable folding
m_stc->SetProperty("fold", "1");
m_stc->SetFoldFlags(wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED);
mainSizer->Add(m_stc, 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");
// Set initial Python mode with sample content
SetupPythonLexer();
m_stc->SetText(
"# KiCad Python Console Example\n"
"import pcbnew\n"
"\n"
"def list_footprints():\n"
" '''List all footprints on the board'''\n"
" board = pcbnew.GetBoard()\n"
" for fp in board.GetFootprints():\n"
" print(f\"Footprint: {fp.GetReference()}\")\n"
" print(f\" Value: {fp.GetValue()}\")\n"
" print(f\" Position: {fp.GetPosition()}\")\n"
"\n"
"# Call the function\n"
"list_footprints()\n"
);
LogEvent("wxStyledTextCtrl test app started");
LogEvent("Python mode enabled with sample code");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[STC_TEST] wxStyledTextCtrl test app started successfully');
});
#endif
}
void StcTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[STC_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void StcTestFrame::SetupPythonLexer()
{
m_stc->SetLexer(wxSTC_LEX_PYTHON);
// Python keywords
m_stc->SetKeyWords(0, "and as assert async await break class continue def del elif else "
"except finally for from global if import in is lambda nonlocal not "
"or pass raise return try while with yield True False None");
// Styling for Python
m_stc->StyleSetForeground(wxSTC_P_DEFAULT, *wxBLACK);
m_stc->StyleSetForeground(wxSTC_P_COMMENTLINE, wxColour(0, 128, 0)); // Green
m_stc->StyleSetForeground(wxSTC_P_NUMBER, wxColour(128, 0, 128)); // Purple
m_stc->StyleSetForeground(wxSTC_P_STRING, wxColour(0, 0, 128)); // Blue
m_stc->StyleSetForeground(wxSTC_P_CHARACTER, wxColour(0, 0, 128)); // Blue
m_stc->StyleSetForeground(wxSTC_P_WORD, wxColour(0, 0, 255)); // Bright blue
m_stc->StyleSetBold(wxSTC_P_WORD, true);
m_stc->StyleSetForeground(wxSTC_P_TRIPLE, wxColour(127, 0, 0)); // Dark red
m_stc->StyleSetForeground(wxSTC_P_TRIPLEDOUBLE, wxColour(127, 0, 0)); // Dark red
m_stc->StyleSetForeground(wxSTC_P_CLASSNAME, wxColour(0, 128, 128)); // Teal
m_stc->StyleSetBold(wxSTC_P_CLASSNAME, true);
m_stc->StyleSetForeground(wxSTC_P_DEFNAME, wxColour(0, 128, 128)); // Teal
m_stc->StyleSetBold(wxSTC_P_DEFNAME, true);
m_stc->StyleSetForeground(wxSTC_P_OPERATOR, *wxBLACK);
m_stc->StyleSetForeground(wxSTC_P_IDENTIFIER, *wxBLACK);
m_stc->StyleSetForeground(wxSTC_P_DECORATOR, wxColour(255, 128, 0)); // Orange
// Enable Python-specific folding
m_stc->SetProperty("fold.compact", "0");
m_stc->Colourise(0, -1);
LogEvent("Python lexer configured");
}
void StcTestFrame::SetupDrcLexer()
{
// DRC rules are similar to S-expressions - use Lisp lexer
m_stc->SetLexer(wxSTC_LEX_LISP);
// Keywords for DRC rules
m_stc->SetKeyWords(0, "version rule condition constraint layer net type "
"min max opt clearance track_width via_diameter "
"hole_size annular_width silk_clearance courtyward_clearance");
// Styling for DRC (Lisp-like)
m_stc->StyleSetForeground(wxSTC_LISP_DEFAULT, *wxBLACK);
m_stc->StyleSetForeground(wxSTC_LISP_COMMENT, wxColour(0, 128, 0)); // Green
m_stc->StyleSetForeground(wxSTC_LISP_NUMBER, wxColour(128, 0, 128)); // Purple
m_stc->StyleSetForeground(wxSTC_LISP_KEYWORD, wxColour(0, 0, 255)); // Bright blue
m_stc->StyleSetBold(wxSTC_LISP_KEYWORD, true);
m_stc->StyleSetForeground(wxSTC_LISP_STRING, wxColour(0, 0, 128)); // Blue
m_stc->StyleSetForeground(wxSTC_LISP_OPERATOR, wxColour(128, 0, 0)); // Red
m_stc->Colourise(0, -1);
LogEvent("DRC rules lexer configured");
}
void StcTestFrame::SetupPlainText()
{
m_stc->SetLexer(wxSTC_LEX_NULL);
m_stc->StyleSetForeground(wxSTC_STYLE_DEFAULT, *wxBLACK);
m_stc->StyleSetBackground(wxSTC_STYLE_DEFAULT, *wxWHITE);
m_stc->StyleClearAll();
LogEvent("Plain text mode enabled");
}
void StcTestFrame::OnPythonMode(wxCommandEvent& WXUNUSED(evt))
{
SetupPythonLexer();
if (m_stc->GetTextLength() == 0) {
m_stc->SetText(
"# Python code here\n"
"import pcbnew\n"
"\n"
"board = pcbnew.GetBoard()\n"
"print(board)\n"
);
} else {
m_stc->Colourise(0, -1);
}
}
void StcTestFrame::OnDrcMode(wxCommandEvent& WXUNUSED(evt))
{
SetupDrcLexer();
if (m_stc->GetTextLength() == 0) {
m_stc->SetText(
"; KiCad DRC Rules Example\n"
"(version 1)\n"
"\n"
"(rule \"Minimum track width\"\n"
" (condition \"A.Type == 'track'\")\n"
" (constraint track_width (min 0.2mm)))\n"
"\n"
"(rule \"Via size\"\n"
" (condition \"A.Type == 'via'\")\n"
" (constraint via_diameter (min 0.6mm))\n"
" (constraint hole_size (min 0.3mm)))\n"
);
} else {
m_stc->Colourise(0, -1);
}
}
void StcTestFrame::OnPlainMode(wxCommandEvent& WXUNUSED(evt))
{
SetupPlainText();
}
void StcTestFrame::OnInsertSample(wxCommandEvent& WXUNUSED(evt))
{
static int sampleNum = 1;
wxString sample = wxString::Format("\n# Sample insertion %d\nx = %d\nprint(x)\n", sampleNum, sampleNum);
m_stc->AppendText(sample);
LogEvent(wxString::Format("Inserted sample code #%d", sampleNum));
sampleNum++;
}
void StcTestFrame::OnClearText(wxCommandEvent& WXUNUSED(evt))
{
m_stc->ClearAll();
LogEvent("Text cleared");
}
void StcTestFrame::OnShowLineNumbers(wxCommandEvent& WXUNUSED(evt))
{
// Toggle line numbers
if (m_stc->GetMarginWidth(0) > 0) {
m_stc->SetMarginWidth(0, 0);
LogEvent("Line numbers hidden");
} else {
m_stc->SetMarginWidth(0, 40);
LogEvent("Line numbers shown");
}
}
void StcTestFrame::OnFoldCode(wxCommandEvent& WXUNUSED(evt))
{
// Fold all
for (int line = 0; line < m_stc->GetLineCount(); line++) {
int level = m_stc->GetFoldLevel(line);
if (level & wxSTC_FOLDLEVELHEADERFLAG) {
if (m_stc->GetFoldExpanded(line)) {
m_stc->ToggleFold(line);
}
}
}
LogEvent("All code folded");
}
void StcTestFrame::OnStcChange(wxStyledTextEvent& evt)
{
// Don't log every character - too noisy
// Only log significant changes
static int changeCount = 0;
changeCount++;
if (changeCount % 10 == 0) {
LogEvent(wxString::Format("Text changed (%d modifications)", changeCount));
}
evt.Skip();
}
void StcTestFrame::OnStcCharAdded(wxStyledTextEvent& evt)
{
int ch = evt.GetKey();
if (ch == '\n') {
// Auto-indent after newline
int currentLine = m_stc->GetCurrentLine();
if (currentLine > 0) {
int prevLineIndent = m_stc->GetLineIndentation(currentLine - 1);
m_stc->SetLineIndentation(currentLine, prevLineIndent);
m_stc->GotoPos(m_stc->GetLineIndentPosition(currentLine));
}
LogEvent("Auto-indent applied");
}
evt.Skip();
}
void StcTestFrame::OnStcMarginClick(wxStyledTextEvent& evt)
{
int line = m_stc->LineFromPosition(evt.GetPosition());
int margin = evt.GetMargin();
if (margin == 1) { // Folding margin
int level = m_stc->GetFoldLevel(line);
if (level & wxSTC_FOLDLEVELHEADERFLAG) {
m_stc->ToggleFold(line);
LogEvent(wxString::Format("Toggled fold at line %d", line + 1));
}
}
evt.Skip();
}
void StcTestFrame::OnStcUpdateUI(wxStyledTextEvent& evt)
{
// Update status bar with cursor position
int pos = m_stc->GetCurrentPos();
int line = m_stc->GetCurrentLine();
int col = m_stc->GetColumn(pos);
SetStatusText(wxString::Format("Line %d, Col %d", line + 1, col + 1));
evt.Skip();
}

View file

@ -0,0 +1,151 @@
// Text Decorations Test - Tests underline and strikethrough rendering
// Tests: wxFont::SetUnderlined(), wxFont::SetStrikethrough(), wxDC::DrawText()
#include "wx/wx.h"
class TextDecorFrame : public wxFrame
{
public:
TextDecorFrame() : wxFrame(nullptr, wxID_ANY, "Text Decorations Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"Tests underline and strikethrough text rendering.\n"
"Uses wxFont::SetUnderlined() and wxFont::SetStrikethrough().");
mainSizer->Add(desc, 0, wxALL, 5);
// Drawing panel
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 400));
m_drawPanel->SetBackgroundColour(*wxWHITE);
m_drawPanel->Bind(wxEVT_PAINT, &TextDecorFrame::OnPaint, this);
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Text decoration test app started");
Log("Text decoration test app started");
Log("Testing underline, strikethrough, and combined decorations");
}
private:
void OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(m_drawPanel);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
int y = 20;
const int lineHeight = 40;
const int x = 20;
// Section 1: Normal text (no decorations)
dc.SetFont(wxFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc.SetTextForeground(*wxBLACK);
dc.DrawText("Normal text (no decorations)", x, y);
y += lineHeight;
// Section 2: Underlined text
wxFont underlineFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
underlineFont.SetUnderlined(true);
dc.SetFont(underlineFont);
dc.DrawText("Underlined text", x, y);
y += lineHeight;
// Section 3: Strikethrough text
wxFont strikeFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
strikeFont.SetStrikethrough(true);
dc.SetFont(strikeFont);
dc.DrawText("Strikethrough text", x, y);
y += lineHeight;
// Section 4: Both underline and strikethrough
wxFont bothFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
bothFont.SetUnderlined(true);
bothFont.SetStrikethrough(true);
dc.SetFont(bothFont);
dc.DrawText("Both underline and strikethrough", x, y);
y += lineHeight + 10;
// Section 5: Different font sizes with underline
dc.SetTextForeground(wxColour(0, 0, 128)); // Dark blue
int sizes[] = {10, 14, 18, 24, 32};
for (int size : sizes)
{
wxFont sizedFont(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
sizedFont.SetUnderlined(true);
dc.SetFont(sizedFont);
dc.DrawText(wxString::Format("%dpt underlined", size), x, y);
y += size + 12;
}
y += 10;
// Section 6: Different colors with strikethrough
wxColour colors[] = {*wxRED, *wxGREEN, *wxBLUE, wxColour(128, 0, 128)};
const char* colorNames[] = {"Red", "Green", "Blue", "Purple"};
wxFont colorFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
colorFont.SetStrikethrough(true);
dc.SetFont(colorFont);
int xPos = x;
for (int i = 0; i < 4; i++)
{
dc.SetTextForeground(colors[i]);
dc.DrawText(wxString::Format("%s strikethrough", colorNames[i]), xPos, y);
xPos += 180;
}
y += lineHeight;
// Section 7: Bold and italic with decorations
y += 10;
dc.SetTextForeground(*wxBLACK);
wxFont boldUnderline(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
boldUnderline.SetUnderlined(true);
dc.SetFont(boldUnderline);
dc.DrawText("Bold + Underlined", x, y);
wxFont italicStrike(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL);
italicStrike.SetStrikethrough(true);
dc.SetFont(italicStrike);
dc.DrawText("Italic + Strikethrough", x + 250, y);
wxFont boldItalicBoth(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD);
boldItalicBoth.SetUnderlined(true);
boldItalicBoth.SetStrikethrough(true);
dc.SetFont(boldItalicBoth);
dc.DrawText("Bold + Italic + Both", x + 500, y);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxPanel* m_drawPanel;
wxTextCtrl* m_log;
};
class TextDecorApp : public wxApp
{
public:
virtual bool OnInit() override
{
TextDecorFrame* frame = new TextDecorFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(TextDecorApp);

View file

@ -0,0 +1,146 @@
/**
* Thread Pool Deadlock Test
*
* This test replicates KiCad's pattern of creating hardware_concurrency() threads.
* When PTHREAD_POOL_SIZE < hardware_concurrency(), this should deadlock because:
* 1. Threads 1-N use pre-warmed Web Workers
* 2. Thread N+1 needs new Web Worker (posts to event loop)
* 3. Main thread busy-waits for thread to start
* 4. Busy-wait blocks event loop -> Worker message never processed
* 5. DEADLOCK
*/
#include "wx/wx.h"
#include <thread>
#include <vector>
#include <atomic>
#include <chrono>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class ThreadPoolFrame : public wxFrame
{
public:
ThreadPoolFrame()
: wxFrame(nullptr, wxID_ANY, "Thread Pool Deadlock Test",
wxDefaultPosition, wxSize(800, 600))
{
// Log hardware_concurrency - this is what KiCad uses to determine thread count
int num_threads = std::thread::hardware_concurrency();
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] hardware_concurrency: ' + $0);
}, num_threads);
#endif
printf("[THREADPOOL] hardware_concurrency: %d\n", num_threads);
// Replicate KiCad's exact pattern: create hardware_concurrency() threads
// in the constructor (like BS::priority_thread_pool does)
printf("[THREADPOOL] Creating %d threads (like KiCad's BS::priority_thread_pool)...\n", num_threads);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] Creating ' + $0 + ' threads...');
}, num_threads);
#endif
std::vector<std::thread> threads;
std::vector<std::atomic<bool>> started(num_threads);
for (int i = 0; i < num_threads; i++) {
started[i] = false;
printf("[THREADPOOL] Creating thread %d\n", i);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] Creating thread ' + $0);
}, i);
#endif
threads.emplace_back([i, &started]() {
started[i] = true;
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] Thread ' + $0 + ' started');
}, i);
#endif
// Simulate some work
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
}
printf("[THREADPOOL] All threads created, waiting for completion...\n");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] All threads created, waiting for completion...');
});
#endif
// Join all threads (this is what thread pool destructor does)
for (auto& t : threads) {
t.join();
}
printf("[THREADPOOL] SUCCESS - All %d threads completed!\n", num_threads);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] SUCCESS - All threads completed!');
});
#endif
// Create simple UI to show success
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxString msg = wxString::Format(
"Thread Pool Test PASSED!\n\n"
"Created and joined %d threads successfully.\n\n"
"This test replicates KiCad's BS::priority_thread_pool pattern.\n"
"If you see this message, the deadlock did NOT occur.",
num_threads
);
wxStaticText* label = new wxStaticText(panel, wxID_ANY, msg,
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
label->SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
sizer->AddStretchSpacer();
sizer->Add(label, 0, wxALIGN_CENTER | wxALL, 20);
sizer->AddStretchSpacer();
panel->SetSizer(sizer);
CreateStatusBar();
SetStatusText(wxString::Format("SUCCESS: %d threads created and joined", num_threads));
}
};
class ThreadPoolApp : public wxApp
{
public:
virtual bool OnInit() override
{
printf("[THREADPOOL] App OnInit starting\n");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] App OnInit starting');
});
#endif
ThreadPoolFrame* frame = new ThreadPoolFrame();
frame->Show();
printf("[THREADPOOL] App OnInit complete\n");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[THREADPOOL] App OnInit complete');
});
#endif
return true;
}
};
wxIMPLEMENT_APP(ThreadPoolApp);

View file

@ -0,0 +1,235 @@
// wxTimer Test - Tests timer functionality in WASM
// KiCad uses timers for animations, auto-save, and periodic updates
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/timer.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class TimerTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class TimerTestFrame : public wxFrame
{
public:
TimerTestFrame();
~TimerTestFrame();
private:
wxTimer* m_timer;
wxTimer* m_fastTimer;
int m_counter;
int m_fastCounter;
wxStaticText* m_counterDisplay;
wxStaticText* m_fastCounterDisplay;
wxGauge* m_progressGauge;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnStartTimer(wxCommandEvent& evt);
void OnStopTimer(wxCommandEvent& evt);
void OnStartFastTimer(wxCommandEvent& evt);
void OnStopFastTimer(wxCommandEvent& evt);
void OnResetCounters(wxCommandEvent& evt);
void OnTimer(wxTimerEvent& evt);
void OnFastTimer(wxTimerEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_START_TIMER = wxID_HIGHEST + 1,
ID_STOP_TIMER,
ID_START_FAST_TIMER,
ID_STOP_FAST_TIMER,
ID_RESET_COUNTERS,
ID_TIMER,
ID_FAST_TIMER
};
wxBEGIN_EVENT_TABLE(TimerTestFrame, wxFrame)
EVT_BUTTON(ID_START_TIMER, TimerTestFrame::OnStartTimer)
EVT_BUTTON(ID_STOP_TIMER, TimerTestFrame::OnStopTimer)
EVT_BUTTON(ID_START_FAST_TIMER, TimerTestFrame::OnStartFastTimer)
EVT_BUTTON(ID_STOP_FAST_TIMER, TimerTestFrame::OnStopFastTimer)
EVT_BUTTON(ID_RESET_COUNTERS, TimerTestFrame::OnResetCounters)
EVT_TIMER(ID_TIMER, TimerTestFrame::OnTimer)
EVT_TIMER(ID_FAST_TIMER, TimerTestFrame::OnFastTimer)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(TimerTestApp);
bool TimerTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
TimerTestFrame* frame = new TimerTestFrame();
frame->Show(true);
return true;
}
TimerTestFrame::TimerTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxTimer WASM Test",
wxDefaultPosition, wxSize(600, 550))
, m_counter(0)
, m_fastCounter(0)
{
m_timer = new wxTimer(this, ID_TIMER);
m_fastTimer = new wxTimer(this, ID_FAST_TIMER);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxTimer Test\n\n"
"KiCad uses timers for auto-save, animations, and periodic updates.\n"
"Test both slow (1 sec) and fast (100ms) timers.");
mainSizer->Add(desc, 0, wxALL, 10);
// Slow timer section (1 second interval)
wxStaticBoxSizer* timerBox = new wxStaticBoxSizer(wxVERTICAL, this, "Slow Timer (1 second)");
wxBoxSizer* timerBtnSizer = new wxBoxSizer(wxHORIZONTAL);
timerBtnSizer->Add(new wxButton(this, ID_START_TIMER, "Start"), 0, wxALL, 5);
timerBtnSizer->Add(new wxButton(this, ID_STOP_TIMER, "Stop"), 0, wxALL, 5);
timerBox->Add(timerBtnSizer, 0, wxALIGN_CENTER);
m_counterDisplay = new wxStaticText(this, wxID_ANY, "Counter: 0",
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
m_counterDisplay->SetFont(m_counterDisplay->GetFont().Scale(2.0));
timerBox->Add(m_counterDisplay, 0, wxALIGN_CENTER | wxALL, 10);
mainSizer->Add(timerBox, 0, wxEXPAND | wxALL, 10);
// Fast timer section (100ms interval)
wxStaticBoxSizer* fastTimerBox = new wxStaticBoxSizer(wxVERTICAL, this, "Fast Timer (100ms)");
wxBoxSizer* fastBtnSizer = new wxBoxSizer(wxHORIZONTAL);
fastBtnSizer->Add(new wxButton(this, ID_START_FAST_TIMER, "Start Fast"), 0, wxALL, 5);
fastBtnSizer->Add(new wxButton(this, ID_STOP_FAST_TIMER, "Stop Fast"), 0, wxALL, 5);
fastTimerBox->Add(fastBtnSizer, 0, wxALIGN_CENTER);
m_fastCounterDisplay = new wxStaticText(this, wxID_ANY, "Fast Counter: 0");
fastTimerBox->Add(m_fastCounterDisplay, 0, wxALIGN_CENTER | wxALL, 5);
m_progressGauge = new wxGauge(this, wxID_ANY, 100);
fastTimerBox->Add(m_progressGauge, 0, wxEXPAND | wxALL, 5);
mainSizer->Add(fastTimerBox, 0, wxEXPAND | wxALL, 10);
// Reset button
mainSizer->Add(new wxButton(this, ID_RESET_COUNTERS, "Reset All Counters"),
0, wxALIGN_CENTER | wxALL, 5);
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
LogEvent("Timer test app started");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TIMER_TEST] wxTimer test app started successfully');
});
#endif
}
TimerTestFrame::~TimerTestFrame()
{
m_timer->Stop();
m_fastTimer->Stop();
delete m_timer;
delete m_fastTimer;
}
void TimerTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TIMER_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void TimerTestFrame::OnStartTimer(wxCommandEvent& WXUNUSED(evt))
{
m_timer->Start(1000); // 1 second
LogEvent("Slow timer started (1 second interval)");
}
void TimerTestFrame::OnStopTimer(wxCommandEvent& WXUNUSED(evt))
{
m_timer->Stop();
LogEvent("Slow timer stopped");
}
void TimerTestFrame::OnStartFastTimer(wxCommandEvent& WXUNUSED(evt))
{
m_fastTimer->Start(100); // 100ms
LogEvent("Fast timer started (100ms interval)");
}
void TimerTestFrame::OnStopFastTimer(wxCommandEvent& WXUNUSED(evt))
{
m_fastTimer->Stop();
LogEvent("Fast timer stopped");
}
void TimerTestFrame::OnResetCounters(wxCommandEvent& WXUNUSED(evt))
{
m_counter = 0;
m_fastCounter = 0;
m_counterDisplay->SetLabel("Counter: 0");
m_fastCounterDisplay->SetLabel("Fast Counter: 0");
m_progressGauge->SetValue(0);
LogEvent("Counters reset");
}
void TimerTestFrame::OnTimer(wxTimerEvent& WXUNUSED(evt))
{
m_counter++;
m_counterDisplay->SetLabel(wxString::Format("Counter: %d", m_counter));
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TIMER_TICK] Slow timer tick: ' + $0);
}, m_counter);
#endif
}
void TimerTestFrame::OnFastTimer(wxTimerEvent& WXUNUSED(evt))
{
m_fastCounter++;
m_fastCounterDisplay->SetLabel(wxString::Format("Fast Counter: %d", m_fastCounter));
m_progressGauge->SetValue(m_fastCounter % 101);
// Log every 10 ticks to avoid flooding
if (m_fastCounter % 10 == 0) {
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TIMER_TICK] Fast timer tick: ' + $0);
}, m_fastCounter);
#endif
}
}

View file

@ -0,0 +1,188 @@
// wxToolBar/wxStatusBar Test - Tests toolbar and statusbar in WASM
// KiCad uses toolbars extensively for actions
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/artprov.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class ToolbarTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class ToolbarTestFrame : public wxFrame
{
public:
ToolbarTestFrame();
private:
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void OnToolNew(wxCommandEvent& evt);
void OnToolOpen(wxCommandEvent& evt);
void OnToolSave(wxCommandEvent& evt);
void OnToolZoomIn(wxCommandEvent& evt);
void OnToolZoomOut(wxCommandEvent& evt);
void OnToolToggle(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_TOOL_NEW = wxID_HIGHEST + 1,
ID_TOOL_OPEN,
ID_TOOL_SAVE,
ID_TOOL_ZOOM_IN,
ID_TOOL_ZOOM_OUT,
ID_TOOL_TOGGLE
};
wxBEGIN_EVENT_TABLE(ToolbarTestFrame, wxFrame)
EVT_TOOL(ID_TOOL_NEW, ToolbarTestFrame::OnToolNew)
EVT_TOOL(ID_TOOL_OPEN, ToolbarTestFrame::OnToolOpen)
EVT_TOOL(ID_TOOL_SAVE, ToolbarTestFrame::OnToolSave)
EVT_TOOL(ID_TOOL_ZOOM_IN, ToolbarTestFrame::OnToolZoomIn)
EVT_TOOL(ID_TOOL_ZOOM_OUT, ToolbarTestFrame::OnToolZoomOut)
EVT_TOOL(ID_TOOL_TOGGLE, ToolbarTestFrame::OnToolToggle)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(ToolbarTestApp);
bool ToolbarTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
ToolbarTestFrame* frame = new ToolbarTestFrame();
frame->Show(true);
return true;
}
ToolbarTestFrame::ToolbarTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxToolBar/wxStatusBar WASM Test",
wxDefaultPosition, wxSize(700, 500))
{
// Create toolbar
wxToolBar* toolbar = CreateToolBar(wxTB_HORIZONTAL | wxTB_TEXT);
toolbar->AddTool(ID_TOOL_NEW, "New",
wxArtProvider::GetBitmap(wxART_NEW, wxART_TOOLBAR),
"Create new file");
toolbar->AddTool(ID_TOOL_OPEN, "Open",
wxArtProvider::GetBitmap(wxART_FILE_OPEN, wxART_TOOLBAR),
"Open existing file");
toolbar->AddTool(ID_TOOL_SAVE, "Save",
wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_TOOLBAR),
"Save current file");
toolbar->AddSeparator();
toolbar->AddTool(ID_TOOL_ZOOM_IN, "Zoom In",
wxArtProvider::GetBitmap(wxART_PLUS, wxART_TOOLBAR),
"Zoom in");
toolbar->AddTool(ID_TOOL_ZOOM_OUT, "Zoom Out",
wxArtProvider::GetBitmap(wxART_MINUS, wxART_TOOLBAR),
"Zoom out");
toolbar->AddSeparator();
toolbar->AddCheckTool(ID_TOOL_TOGGLE, "Toggle",
wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_TOOLBAR),
wxNullBitmap,
"Toggle tool example");
toolbar->Realize();
// Create status bar with multiple fields
CreateStatusBar(3);
SetStatusText("Ready", 0);
SetStatusText("X: 0, Y: 0", 1);
SetStatusText("Zoom: 100%", 2);
// Set status bar field widths
int widths[] = {-1, 150, 100};
GetStatusBar()->SetStatusWidths(3, widths);
// Main content
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxToolBar and wxStatusBar Test\n\n"
"KiCad uses toolbars for quick access to tools.\n"
"Click toolbar buttons to see events.");
mainSizer->Add(desc, 0, wxALL, 10);
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
LogEvent("Toolbar test app started");
LogEvent("Toolbar created with 6 tools");
LogEvent("Status bar created with 3 fields");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TOOLBAR_TEST] wxToolBar test app started successfully');
});
#endif
}
void ToolbarTestFrame::LogEvent(const wxString& msg)
{
m_log->AppendText(msg + "\n");
SetStatusText(msg, 0);
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TOOLBAR_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
}
void ToolbarTestFrame::OnToolNew(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Toolbar: New clicked");
}
void ToolbarTestFrame::OnToolOpen(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Toolbar: Open clicked");
}
void ToolbarTestFrame::OnToolSave(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Toolbar: Save clicked");
}
void ToolbarTestFrame::OnToolZoomIn(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Toolbar: Zoom In clicked");
SetStatusText("Zoom: 150%", 2);
}
void ToolbarTestFrame::OnToolZoomOut(wxCommandEvent& WXUNUSED(evt))
{
LogEvent("Toolbar: Zoom Out clicked");
SetStatusText("Zoom: 75%", 2);
}
void ToolbarTestFrame::OnToolToggle(wxCommandEvent& evt)
{
bool checked = evt.IsChecked();
LogEvent(wxString::Format("Toolbar: Toggle %s", checked ? "ON" : "OFF"));
}

View file

@ -0,0 +1,256 @@
// wxTreeCtrl Test - Tests tree control in WASM
// KiCad uses tree controls for hierarchy browsers (components, nets, etc.)
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/treectrl.h"
#include "wx/imaglist.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
class TreeTestApp : public wxApp
{
public:
virtual bool OnInit() override;
};
class TreeTestFrame : public wxFrame
{
public:
TreeTestFrame();
private:
wxTreeCtrl* m_tree;
wxTextCtrl* m_log;
void LogEvent(const wxString& msg);
void PopulateTree();
void OnSelChanged(wxTreeEvent& evt);
void OnItemExpanding(wxTreeEvent& evt);
void OnItemCollapsing(wxTreeEvent& evt);
void OnItemActivated(wxTreeEvent& evt);
void OnExpandAll(wxCommandEvent& evt);
void OnCollapseAll(wxCommandEvent& evt);
void OnAddItem(wxCommandEvent& evt);
void OnDeleteItem(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_TREE = wxID_HIGHEST + 1,
ID_EXPAND_ALL,
ID_COLLAPSE_ALL,
ID_ADD_ITEM,
ID_DELETE_ITEM
};
wxBEGIN_EVENT_TABLE(TreeTestFrame, wxFrame)
EVT_TREE_SEL_CHANGED(ID_TREE, TreeTestFrame::OnSelChanged)
EVT_TREE_ITEM_EXPANDING(ID_TREE, TreeTestFrame::OnItemExpanding)
EVT_TREE_ITEM_COLLAPSING(ID_TREE, TreeTestFrame::OnItemCollapsing)
EVT_TREE_ITEM_ACTIVATED(ID_TREE, TreeTestFrame::OnItemActivated)
EVT_BUTTON(ID_EXPAND_ALL, TreeTestFrame::OnExpandAll)
EVT_BUTTON(ID_COLLAPSE_ALL, TreeTestFrame::OnCollapseAll)
EVT_BUTTON(ID_ADD_ITEM, TreeTestFrame::OnAddItem)
EVT_BUTTON(ID_DELETE_ITEM, TreeTestFrame::OnDeleteItem)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(TreeTestApp);
bool TreeTestApp::OnInit()
{
if (!wxApp::OnInit())
return false;
TreeTestFrame* frame = new TreeTestFrame();
frame->Show(true);
return true;
}
TreeTestFrame::TreeTestFrame()
: wxFrame(nullptr, wxID_ANY, "wxTreeCtrl WASM Test",
wxDefaultPosition, wxSize(600, 600))
{
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
"wxTreeCtrl Test\n\n"
"KiCad uses tree controls for hierarchy browsers, component trees, and net lists.\n"
"Click items to select, double-click to activate, +/- to expand/collapse.");
mainSizer->Add(desc, 0, wxALL, 10);
// Button bar
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->Add(new wxButton(this, ID_EXPAND_ALL, "Expand All"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_COLLAPSE_ALL, "Collapse All"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_ADD_ITEM, "Add Item"), 0, wxALL, 5);
btnSizer->Add(new wxButton(this, ID_DELETE_ITEM, "Delete Selected"), 0, wxALL, 5);
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
// Tree control
m_tree = new wxTreeCtrl(this, ID_TREE, wxDefaultPosition, wxSize(-1, 250),
wxTR_DEFAULT_STYLE | wxTR_EDIT_LABELS);
mainSizer->Add(m_tree, 1, wxEXPAND | wxALL, 10);
PopulateTree();
// Event log
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
m_log = new wxTextCtrl(this, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Ready");
LogEvent("Tree test app started");
LogEvent("Tree populated with KiCad-like hierarchy");
#ifdef __EMSCRIPTEN__
EM_ASM({
console.log('[TREE_TEST] wxTreeCtrl test app started successfully');
});
#endif
}
void TreeTestFrame::PopulateTree()
{
// Create a KiCad-like component hierarchy
wxTreeItemId root = m_tree->AddRoot("Project: MyBoard");
// Schematic hierarchy
wxTreeItemId schematic = m_tree->AppendItem(root, "Schematic");
wxTreeItemId sheet1 = m_tree->AppendItem(schematic, "Sheet 1 - Main");
m_tree->AppendItem(sheet1, "U1 - MCU");
m_tree->AppendItem(sheet1, "U2 - Power Regulator");
m_tree->AppendItem(sheet1, "C1-C10 - Capacitors");
m_tree->AppendItem(sheet1, "R1-R20 - Resistors");
wxTreeItemId sheet2 = m_tree->AppendItem(schematic, "Sheet 2 - IO");
m_tree->AppendItem(sheet2, "J1 - USB Connector");
m_tree->AppendItem(sheet2, "J2 - GPIO Header");
m_tree->AppendItem(sheet2, "LED1-LED4 - Status LEDs");
// PCB hierarchy
wxTreeItemId pcb = m_tree->AppendItem(root, "PCB");
wxTreeItemId layers = m_tree->AppendItem(pcb, "Layers");
m_tree->AppendItem(layers, "F.Cu - Front Copper");
m_tree->AppendItem(layers, "B.Cu - Back Copper");
m_tree->AppendItem(layers, "F.SilkS - Front Silkscreen");
m_tree->AppendItem(layers, "B.SilkS - Back Silkscreen");
m_tree->AppendItem(layers, "Edge.Cuts - Board Outline");
wxTreeItemId nets = m_tree->AppendItem(pcb, "Nets");
m_tree->AppendItem(nets, "GND (45 pads)");
m_tree->AppendItem(nets, "VCC (12 pads)");
m_tree->AppendItem(nets, "3V3 (8 pads)");
m_tree->AppendItem(nets, "SDA (4 pads)");
m_tree->AppendItem(nets, "SCL (4 pads)");
// Libraries
wxTreeItemId libraries = m_tree->AppendItem(root, "Libraries");
m_tree->AppendItem(libraries, "Device.lib");
m_tree->AppendItem(libraries, "Connector.lib");
m_tree->AppendItem(libraries, "MCU_ST.lib");
m_tree->Expand(root);
m_tree->Expand(schematic);
m_tree->Expand(pcb);
}
void TreeTestFrame::LogEvent(const wxString& msg)
{
#ifdef __EMSCRIPTEN__
// Always log to console, even during early initialization
EM_ASM({
console.log('[TREE_EVENT] ' + UTF8ToString($0));
}, msg.c_str().AsChar());
#endif
// Guard against events firing before m_log is initialized
if (!m_log)
return;
m_log->AppendText(msg + "\n");
SetStatusText(msg);
}
void TreeTestFrame::OnSelChanged(wxTreeEvent& evt)
{
wxTreeItemId item = evt.GetItem();
if (item.IsOk()) {
LogEvent(wxString::Format("Selection changed: '%s'", m_tree->GetItemText(item)));
}
}
void TreeTestFrame::OnItemExpanding(wxTreeEvent& evt)
{
wxTreeItemId item = evt.GetItem();
if (item.IsOk()) {
LogEvent(wxString::Format("Expanding: '%s'", m_tree->GetItemText(item)));
}
}
void TreeTestFrame::OnItemCollapsing(wxTreeEvent& evt)
{
wxTreeItemId item = evt.GetItem();
if (item.IsOk()) {
LogEvent(wxString::Format("Collapsing: '%s'", m_tree->GetItemText(item)));
}
}
void TreeTestFrame::OnItemActivated(wxTreeEvent& evt)
{
wxTreeItemId item = evt.GetItem();
if (item.IsOk()) {
LogEvent(wxString::Format("Activated (double-click): '%s'", m_tree->GetItemText(item)));
}
}
void TreeTestFrame::OnExpandAll(wxCommandEvent& WXUNUSED(evt))
{
m_tree->ExpandAll();
LogEvent("All items expanded");
}
void TreeTestFrame::OnCollapseAll(wxCommandEvent& WXUNUSED(evt))
{
m_tree->CollapseAll();
LogEvent("All items collapsed");
}
void TreeTestFrame::OnAddItem(wxCommandEvent& WXUNUSED(evt))
{
wxTreeItemId sel = m_tree->GetSelection();
if (sel.IsOk()) {
static int itemNum = 1;
wxTreeItemId newItem = m_tree->AppendItem(sel,
wxString::Format("New Item %d", itemNum++));
m_tree->Expand(sel);
m_tree->SelectItem(newItem);
LogEvent(wxString::Format("Added new item under '%s'", m_tree->GetItemText(sel)));
} else {
LogEvent("No item selected - select a parent first");
}
}
void TreeTestFrame::OnDeleteItem(wxCommandEvent& WXUNUSED(evt))
{
wxTreeItemId sel = m_tree->GetSelection();
if (sel.IsOk() && sel != m_tree->GetRootItem()) {
wxString itemText = m_tree->GetItemText(sel);
m_tree->Delete(sel);
LogEvent(wxString::Format("Deleted item: '%s'", itemText));
} else {
LogEvent("Cannot delete root or no item selected");
}
}

View file

@ -0,0 +1,281 @@
// wxValidator Test - Input validation like KiCad uses
// Tests: wxTextValidator, wxIntegerValidator, wxFloatingPointValidator, custom validators
#include "wx/wx.h"
#include "wx/valtext.h"
#include "wx/valnum.h"
// Custom validator similar to KiCad's NETNAME_VALIDATOR
class NetNameValidator : public wxTextValidator
{
public:
NetNameValidator() : wxTextValidator(wxFILTER_NONE)
{
// Allow alphanumeric, underscore, and some special chars
SetCharIncludes("_+-/");
}
virtual wxObject* Clone() const override
{
return new NetNameValidator(*this);
}
virtual bool Validate(wxWindow* parent) override
{
wxTextCtrl* tc = dynamic_cast<wxTextCtrl*>(GetWindow());
if (!tc) return true;
wxString val = tc->GetValue();
// Net name cannot start with a number
if (!val.IsEmpty() && wxIsdigit(val[0]))
{
wxMessageBox("Net name cannot start with a digit", "Validation Error",
wxOK | wxICON_ERROR, parent);
return false;
}
return wxTextValidator::Validate(parent);
}
};
// Custom validator for footprint names (like FOOTPRINT_NAME_VALIDATOR)
class FootprintNameValidator : public wxTextValidator
{
public:
FootprintNameValidator() : wxTextValidator(wxFILTER_ALPHANUMERIC)
{
SetCharIncludes("_-.");
}
virtual wxObject* Clone() const override
{
return new FootprintNameValidator(*this);
}
};
class ValidatorsFrame : public wxFrame
{
public:
ValidatorsFrame() : wxFrame(nullptr, wxID_ANY, "wxValidator Test",
wxDefaultPosition, wxSize(700, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses validators for input validation in dialogs.\n"
"Tests: wxTextValidator, wxIntegerValidator, wxFloatingPointValidator, custom validators.");
mainSizer->Add(desc, 0, wxALL, 5);
// Grid for input fields
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 10, 10);
gridSizer->AddGrowableCol(1, 1);
// 1. Alpha-numeric validator
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Alphanumeric only:"),
0, wxALIGN_CENTER_VERTICAL);
m_alphaCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
wxTextValidator(wxFILTER_ALPHANUMERIC));
m_alphaCtrl->SetHint("Letters and numbers only");
gridSizer->Add(m_alphaCtrl, 1, wxEXPAND);
// 2. Numeric only validator
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Digits only:"),
0, wxALIGN_CENTER_VERTICAL);
m_digitCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
wxTextValidator(wxFILTER_DIGITS));
m_digitCtrl->SetHint("0-9 only");
gridSizer->Add(m_digitCtrl, 1, wxEXPAND);
// 3. Integer validator with range
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Integer (0-1000):"),
0, wxALIGN_CENTER_VERTICAL);
m_intValue = 100;
wxIntegerValidator<int> intValidator(&m_intValue);
intValidator.SetRange(0, 1000);
m_intCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "100",
wxDefaultPosition, wxDefaultSize, 0,
intValidator);
gridSizer->Add(m_intCtrl, 1, wxEXPAND);
// 4. Float validator with range
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Float (0.0-100.0):"),
0, wxALIGN_CENTER_VERTICAL);
m_floatValue = 50.0;
wxFloatingPointValidator<double> floatValidator(3, &m_floatValue);
floatValidator.SetRange(0.0, 100.0);
m_floatCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "50.0",
wxDefaultPosition, wxDefaultSize, 0,
floatValidator);
gridSizer->Add(m_floatCtrl, 1, wxEXPAND);
// 5. Include chars validator
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Email chars (a-z, @, .):"),
0, wxALIGN_CENTER_VERTICAL);
wxTextValidator emailValidator(wxFILTER_ALPHANUMERIC);
emailValidator.SetCharIncludes("@._-");
m_emailCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
emailValidator);
m_emailCtrl->SetHint("user@example.com");
gridSizer->Add(m_emailCtrl, 1, wxEXPAND);
// 6. Exclude chars validator
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "No spaces allowed:"),
0, wxALIGN_CENTER_VERTICAL);
wxTextValidator noSpaceValidator(wxFILTER_EXCLUDE_CHAR_LIST);
noSpaceValidator.SetCharExcludes(" \t\n");
m_noSpaceCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
noSpaceValidator);
m_noSpaceCtrl->SetHint("No whitespace");
gridSizer->Add(m_noSpaceCtrl, 1, wxEXPAND);
// 7. Net name validator (custom)
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Net name (custom):"),
0, wxALIGN_CENTER_VERTICAL);
m_netNameCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
NetNameValidator());
m_netNameCtrl->SetHint("Cannot start with digit");
gridSizer->Add(m_netNameCtrl, 1, wxEXPAND);
// 8. Footprint name validator (custom)
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Footprint name:"),
0, wxALIGN_CENTER_VERTICAL);
m_footprintCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize, 0,
FootprintNameValidator());
m_footprintCtrl->SetHint("Alphanumeric with _-.");
gridSizer->Add(m_footprintCtrl, 1, wxEXPAND);
mainSizer->Add(gridSizer, 0, wxEXPAND | wxALL, 10);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* btnValidate = new wxButton(mainPanel, wxID_ANY, "Validate All");
wxButton* btnTransfer = new wxButton(mainPanel, wxID_ANY, "Transfer Data");
wxButton* btnClear = new wxButton(mainPanel, wxID_ANY, "Clear All");
btnValidate->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnValidateAll, this);
btnTransfer->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnTransferData, this);
btnClear->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnClearAll, this);
btnSizer->Add(btnValidate, 0, wxRIGHT, 5);
btnSizer->Add(btnTransfer, 0, wxRIGHT, 5);
btnSizer->Add(btnClear, 0);
mainSizer->Add(btnSizer, 0, wxALL, 10);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Validator test app started");
Log("Validator test app started");
Log("Try typing invalid characters - they should be blocked");
}
private:
void OnValidateAll(wxCommandEvent& event)
{
Log("Validating all fields...");
bool allValid = true;
// Validate each control
wxTextCtrl* controls[] = {m_alphaCtrl, m_digitCtrl, m_intCtrl, m_floatCtrl,
m_emailCtrl, m_noSpaceCtrl, m_netNameCtrl, m_footprintCtrl};
const char* names[] = {"Alphanumeric", "Digits", "Integer", "Float",
"Email", "NoSpace", "NetName", "Footprint"};
for (int i = 0; i < 8; i++)
{
wxValidator* validator = controls[i]->GetValidator();
if (validator)
{
bool valid = validator->Validate(this);
Log(wxString::Format(" %s: %s", names[i], valid ? "VALID" : "INVALID"));
if (!valid) allValid = false;
}
}
Log(wxString::Format("Overall result: %s", allValid ? "ALL VALID" : "SOME INVALID"));
if (allValid)
{
wxMessageBox("All fields are valid!", "Validation", wxOK | wxICON_INFORMATION);
}
}
void OnTransferData(wxCommandEvent& event)
{
Log("Transferring data from controls...");
// TransferDataFromWindow updates the bound variables
if (TransferDataFromWindow())
{
Log(wxString::Format(" Integer value: %d", m_intValue));
Log(wxString::Format(" Float value: %.3f", m_floatValue));
Log("Transfer successful");
}
else
{
Log("Transfer failed - validation error");
}
}
void OnClearAll(wxCommandEvent& event)
{
m_alphaCtrl->Clear();
m_digitCtrl->Clear();
m_intCtrl->SetValue("0");
m_floatCtrl->SetValue("0.0");
m_emailCtrl->Clear();
m_noSpaceCtrl->Clear();
m_netNameCtrl->Clear();
m_footprintCtrl->Clear();
Log("All fields cleared");
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxTextCtrl* m_alphaCtrl;
wxTextCtrl* m_digitCtrl;
wxTextCtrl* m_intCtrl;
wxTextCtrl* m_floatCtrl;
wxTextCtrl* m_emailCtrl;
wxTextCtrl* m_noSpaceCtrl;
wxTextCtrl* m_netNameCtrl;
wxTextCtrl* m_footprintCtrl;
wxTextCtrl* m_log;
int m_intValue;
double m_floatValue;
};
class ValidatorsApp : public wxApp
{
public:
virtual bool OnInit() override
{
ValidatorsFrame* frame = new ValidatorsFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(ValidatorsApp);

View file

@ -0,0 +1,461 @@
// WASM Edge Cases Test - Test WASM-specific behaviors and limitations
// Tests: Threading stubs, file system, memory, asyncify, clipboard permissions
#include "wx/wx.h"
#include "wx/file.h"
#include "wx/filename.h"
#include "wx/dir.h"
#include "wx/clipbrd.h"
#include "wx/thread.h"
#include "wx/utils.h"
#include "wx/fontenum.h"
// Test if threading is stubbed or functional
class TestThread : public wxThread
{
public:
TestThread(wxTextCtrl* log) : wxThread(wxTHREAD_DETACHED), m_log(log) {}
virtual void* Entry() override
{
// In WASM, this may not actually run in a separate thread
m_ran = true;
return nullptr;
}
bool DidRun() const { return m_ran; }
private:
wxTextCtrl* m_log;
bool m_ran = false;
};
class WasmEdgeFrame : public wxFrame
{
public:
WasmEdgeFrame() : wxFrame(nullptr, wxID_ANY, "WASM Edge Cases Test",
wxDefaultPosition, wxSize(800, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"Tests WASM-specific behaviors: threading stubs, file limits, memory, asyncify.\n"
"These tests verify WASM port handles browser limitations correctly.");
mainSizer->Add(desc, 0, wxALL, 5);
// Test buttons
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 10, 10);
gridSizer->AddGrowableCol(1, 1);
// File System Tests
wxButton* btnFileWrite = new wxButton(mainPanel, wxID_ANY, "Test File Write (/tmp/)");
btnFileWrite->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileWrite, this);
gridSizer->Add(btnFileWrite, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Write to WASM virtual file system"), 0, wxALIGN_CENTER_VERTICAL);
wxButton* btnFileRead = new wxButton(mainPanel, wxID_ANY, "Test File Read");
btnFileRead->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileRead, this);
gridSizer->Add(btnFileRead, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Read from virtual file system"), 0, wxALIGN_CENTER_VERTICAL);
wxButton* btnDirList = new wxButton(mainPanel, wxID_ANY, "Test Dir Listing");
btnDirList->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestDirListing, this);
gridSizer->Add(btnDirList, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "List /tmp/ directory contents"), 0, wxALIGN_CENTER_VERTICAL);
// Threading Tests
wxButton* btnThread = new wxButton(mainPanel, wxID_ANY, "Test Threading");
btnThread->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestThreading, this);
gridSizer->Add(btnThread, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Check if wxThread is stubbed"), 0, wxALIGN_CENTER_VERTICAL);
// Font Enumeration Tests
wxButton* btnFonts = new wxButton(mainPanel, wxID_ANY, "Test Font Enumeration");
btnFonts->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFontEnum, this);
gridSizer->Add(btnFonts, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Enumerate available fonts (may fail in WASM)"), 0, wxALIGN_CENTER_VERTICAL);
// Clipboard Tests
wxButton* btnClipboard = new wxButton(mainPanel, wxID_ANY, "Test Clipboard");
btnClipboard->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestClipboard, this);
gridSizer->Add(btnClipboard, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test clipboard with asyncify"), 0, wxALIGN_CENTER_VERTICAL);
// Memory Tests
wxButton* btnMemory = new wxButton(mainPanel, wxID_ANY, "Test Memory Allocation");
btnMemory->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestMemory, this);
gridSizer->Add(btnMemory, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test WASM memory growth"), 0, wxALIGN_CENTER_VERTICAL);
// OS Info Tests
wxButton* btnOsInfo = new wxButton(mainPanel, wxID_ANY, "Test OS Info");
btnOsInfo->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestOsInfo, this);
gridSizer->Add(btnOsInfo, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Check wxGetOsVersion (may be stubbed)"), 0, wxALIGN_CENTER_VERTICAL);
// wxLaunchDefaultBrowser Test
wxButton* btnBrowser = new wxButton(mainPanel, wxID_ANY, "Test URL Launch");
btnBrowser->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestBrowserLaunch, this);
gridSizer->Add(btnBrowser, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test wxLaunchDefaultBrowser"), 0, wxALIGN_CENTER_VERTICAL);
// wxFileName Tests
wxButton* btnFileName = new wxButton(mainPanel, wxID_ANY, "Test wxFileName");
btnFileName->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileName, this);
gridSizer->Add(btnFileName, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Path manipulation functions"), 0, wxALIGN_CENTER_VERTICAL);
// Run All Tests
wxButton* btnAll = new wxButton(mainPanel, wxID_ANY, "Run All Tests");
btnAll->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnRunAllTests, this);
gridSizer->Add(btnAll, 0, wxEXPAND);
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Execute all edge case tests"), 0, wxALIGN_CENTER_VERTICAL);
mainSizer->Add(gridSizer, 0, wxEXPAND | wxALL, 10);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test Results"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 300),
wxTE_MULTILINE | wxTE_READONLY);
m_log->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("WASM edge cases test app started");
Log("WASM Edge Cases Test App Started");
Log("====================================\n");
}
private:
void OnTestFileWrite(wxCommandEvent& event)
{
Log("=== File Write Test ===");
wxString testFile = "/tmp/wasm_test_file.txt";
wxString content = "Hello from WASM!\nLine 2\nLine 3";
wxFile file;
if (file.Create(testFile, true)) // true = overwrite
{
if (file.Write(content))
{
Log("SUCCESS: Wrote " + wxString::Format("%zu", content.Length()) + " bytes to " + testFile);
m_testFilePath = testFile;
}
else
{
Log("FAILED: Could not write to file");
}
file.Close();
}
else
{
Log("FAILED: Could not create file " + testFile);
}
Log("");
}
void OnTestFileRead(wxCommandEvent& event)
{
Log("=== File Read Test ===");
if (m_testFilePath.IsEmpty())
{
Log("No test file - run File Write test first");
Log("");
return;
}
wxFile file;
if (file.Open(m_testFilePath))
{
wxString content;
if (file.ReadAll(&content))
{
Log("SUCCESS: Read " + wxString::Format("%zu", content.Length()) + " bytes");
Log("Content:\n" + content);
}
else
{
Log("FAILED: Could not read file content");
}
file.Close();
}
else
{
Log("FAILED: Could not open " + m_testFilePath);
}
Log("");
}
void OnTestDirListing(wxCommandEvent& event)
{
Log("=== Directory Listing Test ===");
wxDir dir("/tmp");
if (dir.IsOpened())
{
Log("Contents of /tmp/:");
wxString filename;
int count = 0;
bool cont = dir.GetFirst(&filename);
while (cont)
{
Log(" " + filename);
count++;
cont = dir.GetNext(&filename);
}
if (count == 0)
Log(" (empty directory)");
else
Log(wxString::Format(" Total: %d files", count));
}
else
{
Log("FAILED: Could not open /tmp/ directory");
}
Log("");
}
void OnTestThreading(wxCommandEvent& event)
{
Log("=== Threading Test ===");
// In WASM, threading may be stubbed
Log("Creating wxThread...");
// Check if we can create a thread (may be no-op in WASM)
#if wxUSE_THREADS
Log("wxUSE_THREADS is defined");
// Note: Actually running threads in WASM is complex
// This test just checks if the API is available
Log("Thread API is available (may be stubbed in WASM)");
Log("WASM typically runs single-threaded");
Log("For async operations, use wxTimer or emscripten_async_*");
#else
Log("wxUSE_THREADS is NOT defined");
#endif
Log("");
}
void OnTestFontEnum(wxCommandEvent& event)
{
Log("=== Font Enumeration Test ===");
class FontEnumerator : public wxFontEnumerator
{
public:
wxArrayString fonts;
virtual bool OnFacename(const wxString& facename) override
{
fonts.Add(facename);
return true; // Continue enumeration
}
};
FontEnumerator enumerator;
bool result = enumerator.EnumerateFacenames();
if (result && enumerator.fonts.GetCount() > 0)
{
Log("SUCCESS: Found " + wxString::Format("%zu", enumerator.fonts.GetCount()) + " fonts:");
for (size_t i = 0; i < wxMin(enumerator.fonts.GetCount(), (size_t)10); i++)
{
Log(" " + enumerator.fonts[i]);
}
if (enumerator.fonts.GetCount() > 10)
Log(" ... and " + wxString::Format("%zu", enumerator.fonts.GetCount() - 10) + " more");
}
else
{
Log("NOTICE: Font enumeration returned false or empty");
Log("This is expected in WASM - fontenum.cpp returns false");
Log("Font pickers should use a predefined font list instead");
}
Log("");
}
void OnTestClipboard(wxCommandEvent& event)
{
Log("=== Clipboard Test ===");
wxString testText = "WASM Clipboard Test " + wxDateTime::Now().FormatISOCombined();
if (wxTheClipboard->Open())
{
// Write
wxTheClipboard->SetData(new wxTextDataObject(testText));
Log("Wrote to clipboard: " + testText);
// Read back
if (wxTheClipboard->IsSupported(wxDF_TEXT))
{
wxTextDataObject data;
if (wxTheClipboard->GetData(data))
{
Log("Read from clipboard: " + data.GetText());
if (data.GetText() == testText)
Log("SUCCESS: Clipboard round-trip works");
else
Log("WARNING: Read text differs from written text");
}
else
{
Log("NOTICE: Could not read clipboard (may need user interaction)");
}
}
wxTheClipboard->Close();
}
else
{
Log("FAILED: Could not open clipboard");
}
Log("");
}
void OnTestMemory(wxCommandEvent& event)
{
Log("=== Memory Allocation Test ===");
// Test small allocation
std::vector<char> small(1024 * 10); // 10 KB
Log("Allocated 10 KB: SUCCESS");
// Test medium allocation
std::vector<char> medium(1024 * 1024); // 1 MB
Log("Allocated 1 MB: SUCCESS");
// Test larger allocation (WASM memory growth)
try
{
std::vector<char> large(1024 * 1024 * 10); // 10 MB
Log("Allocated 10 MB: SUCCESS (WASM memory growth works)");
}
catch (const std::bad_alloc& e)
{
Log("FAILED: Could not allocate 10 MB");
Log(" Error: " + wxString(e.what()));
}
Log("");
}
void OnTestOsInfo(wxCommandEvent& event)
{
Log("=== OS Info Test ===");
int major, minor, micro;
wxOperatingSystemId os = wxGetOsVersion(&major, &minor, &micro);
Log("wxGetOsVersion returned:");
Log(" OS ID: " + wxString::Format("%d", (int)os));
Log(" Version: " + wxString::Format("%d.%d.%d", major, minor, micro));
wxString osDesc = wxGetOsDescription();
Log(" Description: " + osDesc);
// In WASM, these may be stubbed
if (osDesc.IsEmpty() || osDesc == "Unknown")
{
Log("NOTICE: OS info may be stubbed in WASM");
}
Log("");
}
void OnTestBrowserLaunch(wxCommandEvent& event)
{
Log("=== URL Launch Test ===");
// In WASM, this should open in new tab via window.open()
wxString url = "https://www.kicad.org";
Log("Attempting to open: " + url);
bool result = wxLaunchDefaultBrowser(url);
if (result)
Log("SUCCESS: Browser launch returned true");
else
Log("NOTICE: Browser launch returned false (may still work via popup)");
Log("");
}
void OnTestFileName(wxCommandEvent& event)
{
Log("=== wxFileName Test ===");
// Test path manipulation
wxFileName fn("/tmp/test/file.kicad_pcb");
Log("Full path: " + fn.GetFullPath());
Log("Name: " + fn.GetName());
Log("Extension: " + fn.GetExt());
Log("Path: " + fn.GetPath());
Log("Volume: " + fn.GetVolume());
// Test path building
wxFileName fn2;
fn2.AssignDir("/home/user/projects");
fn2.AppendDir("kicad");
fn2.SetFullName("board.kicad_pcb");
Log("Built path: " + fn2.GetFullPath());
// Test relative path
wxFileName relative;
relative.Assign("../designs/board.kicad_pcb");
Log("Is relative: " + wxString(relative.IsRelative() ? "yes" : "no"));
Log("");
}
void OnRunAllTests(wxCommandEvent& event)
{
m_log->Clear();
Log("Running all WASM edge case tests...\n");
OnTestFileWrite(event);
OnTestFileRead(event);
OnTestDirListing(event);
OnTestThreading(event);
OnTestFontEnum(event);
OnTestClipboard(event);
OnTestMemory(event);
OnTestOsInfo(event);
OnTestFileName(event);
Log("=== All Tests Complete ===");
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxTextCtrl* m_log;
wxString m_testFilePath;
};
class WasmEdgeApp : public wxApp
{
public:
virtual bool OnInit() override
{
WasmEdgeFrame* frame = new WasmEdgeFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(WasmEdgeApp);

View file

@ -0,0 +1,290 @@
// wxWizard Test - Footprint Wizard simulation
// Tests wxWizard for step-by-step dialogs
#include "wx/wx.h"
#include "wx/wizard.h"
#include "wx/spinctrl.h"
// Page 1: Package Type Selection
class PackageTypePage : public wxWizardPageSimple
{
public:
PackageTypePage(wxWizard* parent) : wxWizardPageSimple(parent)
{
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 1: Select Package Type"), 0, wxBOTTOM, 10);
sizer->Add(new wxStaticText(this, wxID_ANY, "Choose the type of footprint to create:"), 0, wxBOTTOM, 10);
wxString choices[] = {"QFP (Quad Flat Package)", "BGA (Ball Grid Array)",
"DIP (Dual In-line Package)", "SOT (Small Outline Transistor)",
"SOP (Small Outline Package)"};
m_choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 5, choices);
m_choice->SetSelection(0);
sizer->Add(m_choice, 0, wxEXPAND | wxBOTTOM, 10);
sizer->Add(new wxStaticText(this, wxID_ANY, "Selected: QFP - Common for microcontrollers"), 0);
SetSizer(sizer);
}
wxString GetSelection() const { return m_choice->GetStringSelection(); }
private:
wxChoice* m_choice;
};
// Page 2: Pin Configuration
class PinConfigPage : public wxWizardPageSimple
{
public:
PinConfigPage(wxWizard* parent) : wxWizardPageSimple(parent)
{
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 2: Pin Configuration"), 0, wxBOTTOM, 10);
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 2, 5, 10);
grid->Add(new wxStaticText(this, wxID_ANY, "Number of Pins:"), 0, wxALIGN_CENTER_VERTICAL);
m_pinCount = new wxSpinCtrl(this, wxID_ANY, "48", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 4, 256, 48);
grid->Add(m_pinCount, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Pins Per Side:"), 0, wxALIGN_CENTER_VERTICAL);
m_pinsPerSide = new wxSpinCtrl(this, wxID_ANY, "12", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 64, 12);
grid->Add(m_pinsPerSide, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Pin Pitch (mm):"), 0, wxALIGN_CENTER_VERTICAL);
wxString pitches[] = {"0.4", "0.5", "0.65", "0.8", "1.0", "1.27"};
m_pitch = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6, pitches);
m_pitch->SetSelection(1);
grid->Add(m_pitch, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Pin 1 Position:"), 0, wxALIGN_CENTER_VERTICAL);
wxString positions[] = {"Top-Left", "Bottom-Left", "Top-Right", "Bottom-Right"};
m_pin1Pos = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, positions);
m_pin1Pos->SetSelection(0);
grid->Add(m_pin1Pos, 0);
sizer->Add(grid, 0, wxEXPAND | wxBOTTOM, 10);
SetSizer(sizer);
}
int GetPinCount() const { return m_pinCount->GetValue(); }
wxString GetPitch() const { return m_pitch->GetStringSelection(); }
private:
wxSpinCtrl* m_pinCount;
wxSpinCtrl* m_pinsPerSide;
wxChoice* m_pitch;
wxChoice* m_pin1Pos;
};
// Page 3: Package Dimensions
class DimensionsPage : public wxWizardPageSimple
{
public:
DimensionsPage(wxWizard* parent) : wxWizardPageSimple(parent)
{
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 3: Package Dimensions"), 0, wxBOTTOM, 10);
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 2, 5, 10);
grid->Add(new wxStaticText(this, wxID_ANY, "Package Width (mm):"), 0, wxALIGN_CENTER_VERTICAL);
m_width = new wxTextCtrl(this, wxID_ANY, "7.0");
grid->Add(m_width, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Package Height (mm):"), 0, wxALIGN_CENTER_VERTICAL);
m_height = new wxTextCtrl(this, wxID_ANY, "7.0");
grid->Add(m_height, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Pad Width (mm):"), 0, wxALIGN_CENTER_VERTICAL);
m_padWidth = new wxTextCtrl(this, wxID_ANY, "0.3");
grid->Add(m_padWidth, 0);
grid->Add(new wxStaticText(this, wxID_ANY, "Pad Height (mm):"), 0, wxALIGN_CENTER_VERTICAL);
m_padHeight = new wxTextCtrl(this, wxID_ANY, "1.0");
grid->Add(m_padHeight, 0);
sizer->Add(grid, 0, wxEXPAND | wxBOTTOM, 10);
// Checkbox options
m_addThermal = new wxCheckBox(this, wxID_ANY, "Add thermal pad");
m_addSilkscreen = new wxCheckBox(this, wxID_ANY, "Add silkscreen outline");
m_addCourtyard = new wxCheckBox(this, wxID_ANY, "Add courtyard");
m_addSilkscreen->SetValue(true);
m_addCourtyard->SetValue(true);
sizer->Add(m_addThermal, 0, wxBOTTOM, 5);
sizer->Add(m_addSilkscreen, 0, wxBOTTOM, 5);
sizer->Add(m_addCourtyard, 0);
SetSizer(sizer);
}
private:
wxTextCtrl* m_width;
wxTextCtrl* m_height;
wxTextCtrl* m_padWidth;
wxTextCtrl* m_padHeight;
wxCheckBox* m_addThermal;
wxCheckBox* m_addSilkscreen;
wxCheckBox* m_addCourtyard;
};
// Page 4: Summary
class SummaryPage : public wxWizardPageSimple
{
public:
SummaryPage(wxWizard* parent, PackageTypePage* p1, PinConfigPage* p2)
: wxWizardPageSimple(parent), m_page1(p1), m_page2(p2)
{
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 4: Summary"), 0, wxBOTTOM, 10);
sizer->Add(new wxStaticText(this, wxID_ANY, "Review your footprint settings:"), 0, wxBOTTOM, 10);
m_summary = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
sizer->Add(m_summary, 1, wxEXPAND | wxBOTTOM, 10);
sizer->Add(new wxStaticText(this, wxID_ANY, "Click Finish to create the footprint."), 0);
SetSizer(sizer);
}
virtual bool TransferDataToWindow() override
{
wxString summary;
summary += "Footprint Summary\n";
summary += "=================\n\n";
summary += wxString::Format("Package Type: %s\n", m_page1->GetSelection());
summary += wxString::Format("Pin Count: %d\n", m_page2->GetPinCount());
summary += wxString::Format("Pin Pitch: %s mm\n", m_page2->GetPitch());
summary += "\nDimensions:\n";
summary += " Package: 7.0 x 7.0 mm\n";
summary += " Pad Size: 0.3 x 1.0 mm\n";
summary += "\nOptions:\n";
summary += " Silkscreen: Yes\n";
summary += " Courtyard: Yes\n";
m_summary->SetValue(summary);
return true;
}
private:
PackageTypePage* m_page1;
PinConfigPage* m_page2;
wxTextCtrl* m_summary;
};
class WizardFrame : public wxFrame
{
public:
WizardFrame() : wxFrame(nullptr, wxID_ANY, "wxWizard Test",
wxDefaultPosition, wxSize(800, 500))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxWizard for the Footprint Wizard.\n"
"This tests step-by-step dialog functionality with Next/Back navigation.");
mainSizer->Add(desc, 0, wxALL, 10);
// Launch button
wxButton* btnLaunch = new wxButton(mainPanel, wxID_ANY, "Launch Footprint Wizard");
btnLaunch->Bind(wxEVT_BUTTON, &WizardFrame::OnLaunchWizard, this);
mainSizer->Add(btnLaunch, 0, wxALL, 10);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 10);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 10);
mainPanel->SetSizer(mainSizer);
// Status bar
CreateStatusBar();
SetStatusText("Wizard test app started");
Log("Wizard test app started");
Log("Click 'Launch Footprint Wizard' to start");
}
private:
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
void OnLaunchWizard(wxCommandEvent& event)
{
Log("Launching Footprint Wizard...");
wxWizard* wizard = new wxWizard(this, wxID_ANY, "Footprint Wizard",
wxNullBitmap, wxDefaultPosition,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
// Create pages
PackageTypePage* page1 = new PackageTypePage(wizard);
PinConfigPage* page2 = new PinConfigPage(wizard);
DimensionsPage* page3 = new DimensionsPage(wizard);
SummaryPage* page4 = new SummaryPage(wizard, page1, page2);
// Chain pages
wxWizardPageSimple::Chain(page1, page2);
wxWizardPageSimple::Chain(page2, page3);
wxWizardPageSimple::Chain(page3, page4);
wizard->GetPageAreaSizer()->Add(page1);
// Bind wizard events
wizard->Bind(wxEVT_WIZARD_PAGE_CHANGED, [this](wxWizardEvent& evt) {
Log(wxString::Format("Page changed to: %d", evt.GetPage() ? 1 : 0));
});
wizard->Bind(wxEVT_WIZARD_CANCEL, [this](wxWizardEvent&) {
Log("Wizard cancelled");
});
wizard->Bind(wxEVT_WIZARD_FINISHED, [this](wxWizardEvent&) {
Log("Wizard finished - footprint would be created");
});
if (wizard->RunWizard(page1))
{
Log("Footprint created successfully!");
Log(wxString::Format(" Type: %s", page1->GetSelection()));
Log(wxString::Format(" Pins: %d", page2->GetPinCount()));
Log(wxString::Format(" Pitch: %s mm", page2->GetPitch()));
}
else
{
Log("Wizard was cancelled");
}
wizard->Destroy();
}
wxTextCtrl* m_log;
};
class WizardApp : public wxApp
{
public:
virtual bool OnInit() override
{
WizardFrame* frame = new WizardFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(WizardApp);

View file

@ -0,0 +1,389 @@
// wxXmlDocument Test - XML parsing like KiCad's config and project files
// Tests: wxXmlDocument, wxXmlNode, parsing, creation, traversal
#include "wx/wx.h"
#include "wx/xml/xml.h"
#include "wx/sstream.h"
class XmlFrame : public wxFrame
{
public:
XmlFrame() : wxFrame(nullptr, wxID_ANY, "wxXmlDocument Test",
wxDefaultPosition, wxSize(900, 700))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxXmlDocument for config/project files (665 occurrences).\n"
"Tests: Parsing, node traversal, creation, modification, serialization.");
mainSizer->Add(desc, 0, wxALL, 5);
// Sample XML
wxStaticBoxSizer* sampleSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Sample XML (KiCad-like project)");
m_xmlInput = new wxTextCtrl(mainPanel, wxID_ANY, GetSampleXml(),
wxDefaultPosition, wxSize(-1, 150),
wxTE_MULTILINE | wxTE_DONTWRAP);
m_xmlInput->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
sampleSizer->Add(m_xmlInput, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(sampleSizer, 0, wxEXPAND | wxALL, 5);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* parseBtn = new wxButton(mainPanel, wxID_ANY, "Parse XML");
parseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnParseXml, this);
btnSizer->Add(parseBtn, 0, wxRIGHT, 5);
wxButton* traverseBtn = new wxButton(mainPanel, wxID_ANY, "Traverse Nodes");
traverseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnTraverseNodes, this);
btnSizer->Add(traverseBtn, 0, wxRIGHT, 5);
wxButton* createBtn = new wxButton(mainPanel, wxID_ANY, "Create XML");
createBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnCreateXml, this);
btnSizer->Add(createBtn, 0, wxRIGHT, 5);
wxButton* modifyBtn = new wxButton(mainPanel, wxID_ANY, "Modify XML");
modifyBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnModifyXml, this);
btnSizer->Add(modifyBtn, 0, wxRIGHT, 5);
wxButton* serializeBtn = new wxButton(mainPanel, wxID_ANY, "Serialize");
serializeBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnSerializeXml, this);
btnSizer->Add(serializeBtn, 0);
mainSizer->Add(btnSizer, 0, wxALL, 5);
// Results
wxStaticBoxSizer* resultSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Results / Output");
m_output = new wxTextCtrl(mainPanel, wxID_ANY, "",
wxDefaultPosition, wxSize(-1, 200),
wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP);
m_output->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
resultSizer->Add(m_output, 1, wxEXPAND | wxALL, 5);
mainSizer->Add(resultSizer, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("XML test app started");
Log("XML test app started");
}
private:
wxString GetSampleXml()
{
return
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<kicad_project version=\"1\">\n"
" <general>\n"
" <name>MyProject</name>\n"
" <version>8.0</version>\n"
" </general>\n"
" <schematic>\n"
" <drawing>\n"
" <sheet name=\"Root\" page=\"A4\"/>\n"
" </drawing>\n"
" </schematic>\n"
" <pcb>\n"
" <layers count=\"4\">\n"
" <layer id=\"0\" name=\"F.Cu\" type=\"copper\"/>\n"
" <layer id=\"1\" name=\"In1.Cu\" type=\"copper\"/>\n"
" <layer id=\"2\" name=\"In2.Cu\" type=\"copper\"/>\n"
" <layer id=\"31\" name=\"B.Cu\" type=\"copper\"/>\n"
" </layers>\n"
" <design_rules>\n"
" <track_width min=\"0.2\" default=\"0.25\"/>\n"
" <via_size min=\"0.4\" default=\"0.8\"/>\n"
" </design_rules>\n"
" </pcb>\n"
"</kicad_project>\n";
}
void OnParseXml(wxCommandEvent& event)
{
Log("Parsing XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
Log("Parse failed");
return;
}
wxXmlNode* root = doc.GetRoot();
if (!root)
{
Output("ERROR: No root element");
return;
}
Output(wxString::Format("Parse successful!\n"));
Output(wxString::Format("Root element: <%s>\n", root->GetName()));
Output(wxString::Format("Version attribute: %s\n", root->GetAttribute("version", "none")));
// Count children
int childCount = 0;
wxXmlNode* child = root->GetChildren();
while (child)
{
if (child->GetType() == wxXML_ELEMENT_NODE)
childCount++;
child = child->GetNext();
}
Output(wxString::Format("Child elements: %d\n", childCount));
Log("Parse complete");
}
void OnTraverseNodes(wxCommandEvent& event)
{
Log("Traversing XML nodes...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
return;
}
wxXmlNode* root = doc.GetRoot();
TraverseNode(root, 0);
Log("Traversal complete");
}
void TraverseNode(wxXmlNode* node, int depth)
{
if (!node) return;
wxString indent(depth * 2, ' ');
if (node->GetType() == wxXML_ELEMENT_NODE)
{
wxString attrs;
wxXmlAttribute* attr = node->GetAttributes();
while (attr)
{
attrs += wxString::Format(" %s=\"%s\"", attr->GetName(), attr->GetValue());
attr = attr->GetNext();
}
wxString content = node->GetNodeContent().Trim();
if (!content.IsEmpty())
{
Output(wxString::Format("%s<%s%s>%s</%s>\n",
indent, node->GetName(), attrs, content, node->GetName()));
}
else
{
Output(wxString::Format("%s<%s%s>\n", indent, node->GetName(), attrs));
// Traverse children
wxXmlNode* child = node->GetChildren();
while (child)
{
TraverseNode(child, depth + 1);
child = child->GetNext();
}
if (node->GetChildren())
Output(wxString::Format("%s</%s>\n", indent, node->GetName()));
}
}
}
void OnCreateXml(wxCommandEvent& event)
{
Log("Creating new XML document...");
m_output->Clear();
// Create a new document
wxXmlDocument doc;
doc.SetVersion("1.0");
doc.SetFileEncoding("UTF-8");
// Create root element
wxXmlNode* root = new wxXmlNode(wxXML_ELEMENT_NODE, "component");
root->AddAttribute("type", "resistor");
doc.SetRoot(root);
// Add child elements
wxXmlNode* refNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "reference");
refNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "R1"));
wxXmlNode* valueNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "value");
valueNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "10k"));
wxXmlNode* footprintNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "footprint");
footprintNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "Resistor_SMD:R_0402"));
// Properties
wxXmlNode* propsNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "properties");
wxXmlNode* prop1 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
prop1->AddAttribute("name", "tolerance");
prop1->AddAttribute("value", "1%");
wxXmlNode* prop2 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
prop2->AddAttribute("name", "power");
prop2->AddAttribute("value", "0.1W");
// Serialize to string
wxStringOutputStream outStream;
if (doc.Save(outStream))
{
Output("Created XML document:\n\n");
Output(outStream.GetString());
}
else
{
Output("ERROR: Failed to serialize");
}
Log("XML creation complete");
}
void OnModifyXml(wxCommandEvent& event)
{
Log("Modifying XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream stream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(stream))
{
Output("ERROR: Failed to parse XML");
return;
}
wxXmlNode* root = doc.GetRoot();
// Find and modify the general/name element
wxXmlNode* general = FindChild(root, "general");
if (general)
{
wxXmlNode* name = FindChild(general, "name");
if (name && name->GetChildren())
{
wxString oldName = name->GetNodeContent();
name->GetChildren()->SetContent("ModifiedProject");
Output(wxString::Format("Changed project name: '%s' -> 'ModifiedProject'\n", oldName));
}
}
// Add a new element to pcb
wxXmlNode* pcb = FindChild(root, "pcb");
if (pcb)
{
wxXmlNode* newElem = new wxXmlNode(wxXML_ELEMENT_NODE, "modified");
newElem->AddAttribute("timestamp", wxDateTime::Now().FormatISOCombined());
pcb->AddChild(newElem);
Output("Added <modified> element to <pcb>\n");
}
// Serialize modified document
wxStringOutputStream outStream;
if (doc.Save(outStream))
{
Output("\nModified XML:\n");
Output(outStream.GetString());
}
Log("Modification complete");
}
void OnSerializeXml(wxCommandEvent& event)
{
Log("Serializing XML...");
m_output->Clear();
wxString xmlStr = m_xmlInput->GetValue();
wxStringInputStream inStream(xmlStr);
wxXmlDocument doc;
if (!doc.Load(inStream))
{
Output("ERROR: Failed to parse XML");
return;
}
// Serialize with formatting
wxStringOutputStream outStream;
if (doc.Save(outStream, 2)) // Indent with 2 spaces
{
Output("Serialized XML (formatted):\n\n");
Output(outStream.GetString());
// Show statistics
wxString serialized = outStream.GetString();
Output(wxString::Format("\n--- Statistics ---\n"));
Output(wxString::Format("Total size: %zu bytes\n", serialized.Length()));
Output(wxString::Format("Lines: %d\n", serialized.Freq('\n') + 1));
}
Log("Serialization complete");
}
wxXmlNode* FindChild(wxXmlNode* parent, const wxString& name)
{
if (!parent) return nullptr;
wxXmlNode* child = parent->GetChildren();
while (child)
{
if (child->GetType() == wxXML_ELEMENT_NODE && child->GetName() == name)
return child;
child = child->GetNext();
}
return nullptr;
}
void Output(const wxString& text)
{
m_output->AppendText(text);
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
wxTextCtrl* m_xmlInput;
wxTextCtrl* m_output;
wxTextCtrl* m_log;
};
class XmlApp : public wxApp
{
public:
virtual bool OnInit() override
{
XmlFrame* frame = new XmlFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(XmlApp);