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:
parent
c4cd69f28d
commit
a26b40c063
78 changed files with 38 additions and 128055 deletions
143
tests/apps/standalone/aui/aui_test.cpp
Normal file
143
tests/apps/standalone/aui/aui_test.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
244
tests/apps/standalone/auinotebook/auinotebook_test.cpp
Normal file
244
tests/apps/standalone/auinotebook/auinotebook_test.cpp
Normal 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);
|
||||
280
tests/apps/standalone/bitmapbuttons/bitmapbuttons_test.cpp
Normal file
280
tests/apps/standalone/bitmapbuttons/bitmapbuttons_test.cpp
Normal 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);
|
||||
206
tests/apps/standalone/bitmask/bitmask_test.cpp
Normal file
206
tests/apps/standalone/bitmask/bitmask_test.cpp
Normal 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);
|
||||
213
tests/apps/standalone/calendar/calendar_test.cpp
Normal file
213
tests/apps/standalone/calendar/calendar_test.cpp
Normal 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);
|
||||
213
tests/apps/standalone/clipboard/clipboard_test.cpp
Normal file
213
tests/apps/standalone/clipboard/clipboard_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
252
tests/apps/standalone/collapsible/collapsible_test.cpp
Normal file
252
tests/apps/standalone/collapsible/collapsible_test.cpp
Normal 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");
|
||||
}
|
||||
459
tests/apps/standalone/dataview/dataview_test.cpp
Normal file
459
tests/apps/standalone/dataview/dataview_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
306
tests/apps/standalone/dataviewvirtual/dataviewvirtual_test.cpp
Normal file
306
tests/apps/standalone/dataviewvirtual/dataviewvirtual_test.cpp
Normal 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);
|
||||
225
tests/apps/standalone/dialog/dialog_test.cpp
Normal file
225
tests/apps/standalone/dialog/dialog_test.cpp
Normal 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);
|
||||
}
|
||||
232
tests/apps/standalone/dnd/dnd_test.cpp
Normal file
232
tests/apps/standalone/dnd/dnd_test.cpp
Normal 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");
|
||||
}
|
||||
154
tests/apps/standalone/earlysize/earlysize_test.cpp
Normal file
154
tests/apps/standalone/earlysize/earlysize_test.cpp
Normal 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(¤tClient.x, ¤tClient.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
|
||||
}
|
||||
}
|
||||
162
tests/apps/standalone/filedialog/filedialog_test.cpp
Normal file
162
tests/apps/standalone/filedialog/filedialog_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
250
tests/apps/standalone/fontenum/fontenum_test.cpp
Normal file
250
tests/apps/standalone/fontenum/fontenum_test.cpp
Normal 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);
|
||||
219
tests/apps/standalone/grid/grid_test.cpp
Normal file
219
tests/apps/standalone/grid/grid_test.cpp
Normal 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();
|
||||
}
|
||||
206
tests/apps/standalone/gridedit/gridedit_test.cpp
Normal file
206
tests/apps/standalone/gridedit/gridedit_test.cpp
Normal 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);
|
||||
330
tests/apps/standalone/gridrenderers/gridrenderers_test.cpp
Normal file
330
tests/apps/standalone/gridrenderers/gridrenderers_test.cpp
Normal 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);
|
||||
346
tests/apps/standalone/htmlwin/htmlwin_test.cpp
Normal file
346
tests/apps/standalone/htmlwin/htmlwin_test.cpp
Normal 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");
|
||||
}
|
||||
204
tests/apps/standalone/infobar/infobar_test.cpp
Normal file
204
tests/apps/standalone/infobar/infobar_test.cpp
Normal 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);
|
||||
}
|
||||
142
tests/apps/standalone/layout/layout_test.cpp
Normal file
142
tests/apps/standalone/layout/layout_test.cpp
Normal 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();
|
||||
}
|
||||
335
tests/apps/standalone/listctrl/listctrl_test.cpp
Normal file
335
tests/apps/standalone/listctrl/listctrl_test.cpp
Normal 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));
|
||||
}
|
||||
215
tests/apps/standalone/logerror/logerror_test.cpp
Normal file
215
tests/apps/standalone/logerror/logerror_test.cpp
Normal 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");
|
||||
}
|
||||
179
tests/apps/standalone/maximize/maximize_test.cpp
Normal file
179
tests/apps/standalone/maximize/maximize_test.cpp
Normal 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);
|
||||
}
|
||||
300
tests/apps/standalone/menu/menu_test.cpp
Normal file
300
tests/apps/standalone/menu/menu_test.cpp
Normal 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");
|
||||
}
|
||||
330
tests/apps/standalone/ownerdrawn/ownerdrawn_test.cpp
Normal file
330
tests/apps/standalone/ownerdrawn/ownerdrawn_test.cpp
Normal 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);
|
||||
205
tests/apps/standalone/pickers/pickers_test.cpp
Normal file
205
tests/apps/standalone/pickers/pickers_test.cpp
Normal 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"));
|
||||
}
|
||||
364
tests/apps/standalone/popup/popup_test.cpp
Normal file
364
tests/apps/standalone/popup/popup_test.cpp
Normal 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);
|
||||
496
tests/apps/standalone/print/print_test.cpp
Normal file
496
tests/apps/standalone/print/print_test.cpp
Normal 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
|
||||
324
tests/apps/standalone/printpreview/printpreview_test.cpp
Normal file
324
tests/apps/standalone/printpreview/printpreview_test.cpp
Normal 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);
|
||||
246
tests/apps/standalone/propgrid/propgrid_test.cpp
Normal file
246
tests/apps/standalone/propgrid/propgrid_test.cpp
Normal 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()));
|
||||
}
|
||||
}
|
||||
235
tests/apps/standalone/regions/regions_test.cpp
Normal file
235
tests/apps/standalone/regions/regions_test.cpp
Normal 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);
|
||||
278
tests/apps/standalone/specialized/specialized_test.cpp
Normal file
278
tests/apps/standalone/specialized/specialized_test.cpp
Normal 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);
|
||||
405
tests/apps/standalone/stc/stc_test.cpp
Normal file
405
tests/apps/standalone/stc/stc_test.cpp
Normal 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();
|
||||
}
|
||||
151
tests/apps/standalone/textdecor/textdecor_test.cpp
Normal file
151
tests/apps/standalone/textdecor/textdecor_test.cpp
Normal 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);
|
||||
146
tests/apps/standalone/threadpool/threadpool_test.cpp
Normal file
146
tests/apps/standalone/threadpool/threadpool_test.cpp
Normal 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);
|
||||
235
tests/apps/standalone/timer/timer_test.cpp
Normal file
235
tests/apps/standalone/timer/timer_test.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
188
tests/apps/standalone/toolbar/toolbar_test.cpp
Normal file
188
tests/apps/standalone/toolbar/toolbar_test.cpp
Normal 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"));
|
||||
}
|
||||
256
tests/apps/standalone/tree/tree_test.cpp
Normal file
256
tests/apps/standalone/tree/tree_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
281
tests/apps/standalone/validators/validators_test.cpp
Normal file
281
tests/apps/standalone/validators/validators_test.cpp
Normal 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);
|
||||
461
tests/apps/standalone/wasmedge/wasmedge_test.cpp
Normal file
461
tests/apps/standalone/wasmedge/wasmedge_test.cpp
Normal 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, µ);
|
||||
|
||||
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);
|
||||
290
tests/apps/standalone/wizard/wizard_test.cpp
Normal file
290
tests/apps/standalone/wizard/wizard_test.cpp
Normal 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);
|
||||
389
tests/apps/standalone/xml/xml_test.cpp
Normal file
389
tests/apps/standalone/xml/xml_test.cpp
Normal 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);
|
||||
Loading…
Reference in a new issue