Reorganize project structure for clarity

- Rename tests/wasm-app/ to tests/apps/ for brevity
- Move lib/gl_immediate_shim.js to wasm/shims/ (consolidates WASM files)
- Delete docs/ directory (outdated Nov 2024 research docs)
- Delete patches/ directory (already applied to fork submodules)
- Delete wasm/patches/ and wasm/config/ (empty/unused)
- Update all file references in scripts, configs, and documentation
- Update .gitignore for new tests/apps/ paths

Verified both build workflows pass:
- wxWidgets tests: 255 passed
- Docker KiCad build: completed successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-12-27 14:06:23 +01:00
commit a26b40c063
78 changed files with 38 additions and 128055 deletions

495
tests/apps/Makefile.wasm Normal file
View file

@ -0,0 +1,495 @@
# Makefile for wxWidgets WASM test apps
# Uses the local wxWidgets build
#
# Usage:
# make -f Makefile.wasm # Optimized release build
# make -f Makefile.wasm DEBUG=1 # Debug build with DWARF symbols and source maps
WXCONFIG = ../../build-wasm/wxwidgets-universal/wx-config
TOOLS_ROOT = ../../wxwidgets/build/wasm
CXX = em++
WX_CXXFLAGS := $(shell $(WXCONFIG) --cxxflags)
# Libraries for GL apps (minimal_test uses OpenGL)
WX_LDFLAGS_GL := $(shell $(WXCONFIG) --libs base,core,gl,aui)
# Libraries for non-GL apps (standalone tests don't need GL)
WX_LDFLAGS_NOGL := $(shell $(WXCONFIG) --libs base,core,aui)
# Libraries for HTML apps (htmlwin_test needs html)
WX_LDFLAGS_HTML := $(shell $(WXCONFIG) --libs base,core,html)
# Libraries for STC apps (stc_test needs stc)
WX_LDFLAGS_STC := $(shell $(WXCONFIG) --libs base,core,stc)
# Libraries for PropGrid apps (propgrid_test needs propgrid)
WX_LDFLAGS_PROPGRID := $(shell $(WXCONFIG) --libs base,core,propgrid)
# Libraries for AUI apps (auinotebook needs aui)
WX_LDFLAGS_AUI := $(shell $(WXCONFIG) --libs base,core,aui)
# Libraries for ADV apps (wizard, calendar need adv)
WX_LDFLAGS_ADV := $(shell $(WXCONFIG) --libs base,core,adv)
# Libraries for XML apps (xml_test needs xml)
WX_LDFLAGS_XML := $(shell $(WXCONFIG) --libs base,core,xml)
# Debug or Release build
ifdef DEBUG
# Debug: DWARF info, source maps, minimal optimization
CXXFLAGS = -g -O0 $(WX_CXXFLAGS)
DEBUG_LDFLAGS = -g -gsource-map
else
# Release: optimized
CXXFLAGS = -O2 $(WX_CXXFLAGS)
DEBUG_LDFLAGS =
endif
# Base Emscripten flags (for all apps)
# ASYNCIFY enables blocking modal dialogs (ShowModal waits for user)
# ASYNCIFY_IMPORTS tells Emscripten which imported JS functions can unwind the stack
# - startModal: for modal dialogs
# - js_writeTextToClipboard, js_readTextFromClipboard, js_clipboardHasText, js_clearClipboard: for clipboard
# - js_enumerateFonts: for font enumeration via Local Font Access API
BASE_LDFLAGS = -sALLOW_MEMORY_GROWTH -sERROR_ON_UNDEFINED_SYMBOLS=0 \
-s "EXPORTED_RUNTIME_METHODS=['HEAPU8','HEAP8','HEAP32','ccall']" \
-sASYNCIFY=1 \
-sASYNCIFY_STACK_SIZE=8192 \
-sASYNCIFY_IMPORTS=['startModal','js_writeTextToClipboard','js_readTextFromClipboard','js_clipboardHasText','js_clearClipboard','js_enumerateFonts']
# GL-specific flags
EM_GL_FLAGS = -sLEGACY_GL_EMULATION -sMAX_WEBGL_VERSION=2
GL_SHIM = ../../wasm/shims/gl_immediate_shim.js
# LDFLAGS for GL apps (minimal_test)
LDFLAGS_GL = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(EM_GL_FLAGS) --js-library=$(GL_SHIM) $(WX_LDFLAGS_GL)
# LDFLAGS for non-GL apps (standalone tests)
LDFLAGS_NOGL = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_NOGL)
# LDFLAGS for HTML apps (htmlwin_test)
LDFLAGS_HTML = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_HTML)
# LDFLAGS for STC apps (stc_test)
LDFLAGS_STC = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_STC)
# LDFLAGS for PropGrid apps (propgrid_test)
LDFLAGS_PROPGRID = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_PROPGRID)
# LDFLAGS for AUI apps (auinotebook)
LDFLAGS_AUI = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_AUI)
# LDFLAGS for ADV apps (wizard, calendar)
LDFLAGS_ADV = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_ADV)
# LDFLAGS for XML apps (xml_test)
LDFLAGS_XML = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(WX_LDFLAGS_XML)
# LDFLAGS for pthread test - uses dynamic PTHREAD_POOL_SIZE to match hardware_concurrency
# This fixes the KiCad deadlock: pre-warm enough workers for all threads
LDFLAGS_PTHREAD = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) -pthread \
-sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' \
-sPTHREAD_POOL_SIZE_STRICT=0 \
$(WX_LDFLAGS_NOGL)
JS = $(TOOLS_ROOT)/wx.js
HTML = $(TOOLS_ROOT)/template.html
# Standalone directories
S = standalone
all: minimal_test.html \
$(S)/menu/menu_test.html \
$(S)/clipboard/clipboard_test.html \
$(S)/filedialog/filedialog_test.html \
$(S)/layout/layout_test.html \
$(S)/aui/aui_test.html \
$(S)/toolbar/toolbar_test.html \
$(S)/grid/grid_test.html \
$(S)/dialog/dialog_test.html \
$(S)/timer/timer_test.html \
$(S)/tree/tree_test.html \
$(S)/dataview/dataview_test.html \
$(S)/htmlwin/htmlwin_test.html \
$(S)/stc/stc_test.html \
$(S)/print/print_test.html \
$(S)/dnd/dnd_test.html \
$(S)/propgrid/propgrid_test.html \
$(S)/pickers/pickers_test.html \
$(S)/collapsible/collapsible_test.html \
$(S)/listctrl/listctrl_test.html \
$(S)/infobar/infobar_test.html \
$(S)/dataviewvirtual/dataviewvirtual_test.html \
$(S)/auinotebook/auinotebook_test.html \
$(S)/wizard/wizard_test.html \
$(S)/gridedit/gridedit_test.html \
$(S)/calendar/calendar_test.html \
$(S)/gridrenderers/gridrenderers_test.html \
$(S)/printpreview/printpreview_test.html \
$(S)/bitmapbuttons/bitmapbuttons_test.html \
$(S)/specialized/specialized_test.html \
$(S)/validators/validators_test.html \
$(S)/ownerdrawn/ownerdrawn_test.html \
$(S)/popup/popup_test.html \
$(S)/xml/xml_test.html \
$(S)/wasmedge/wasmedge_test.html \
$(S)/fontenum/fontenum_test.html \
$(S)/textdecor/textdecor_test.html \
$(S)/bitmask/bitmask_test.html \
$(S)/regions/regions_test.html \
$(S)/maximize/maximize_test.html \
$(S)/earlysize/earlysize_test.html \
$(S)/threadpool/threadpool_test.html \
$(S)/logerror/logerror_test.html
# Main test app (uses GL)
minimal_test.o: minimal_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
minimal_test.html: minimal_test.o
$(CXX) $< $(LDFLAGS_GL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Menu test (no GL)
$(S)/menu/menu_test.o: $(S)/menu/menu_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/menu/menu_test.html: $(S)/menu/menu_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Clipboard test (no GL)
$(S)/clipboard/clipboard_test.o: $(S)/clipboard/clipboard_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/clipboard/clipboard_test.html: $(S)/clipboard/clipboard_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# FileDialog test (no GL)
$(S)/filedialog/filedialog_test.o: $(S)/filedialog/filedialog_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/filedialog/filedialog_test.html: $(S)/filedialog/filedialog_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Layout test (no GL)
$(S)/layout/layout_test.o: $(S)/layout/layout_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/layout/layout_test.html: $(S)/layout/layout_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# AUI test (no GL)
$(S)/aui/aui_test.o: $(S)/aui/aui_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/aui/aui_test.html: $(S)/aui/aui_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Toolbar test (no GL)
$(S)/toolbar/toolbar_test.o: $(S)/toolbar/toolbar_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/toolbar/toolbar_test.html: $(S)/toolbar/toolbar_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Grid test (no GL)
$(S)/grid/grid_test.o: $(S)/grid/grid_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/grid/grid_test.html: $(S)/grid/grid_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Dialog test (no GL)
$(S)/dialog/dialog_test.o: $(S)/dialog/dialog_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/dialog/dialog_test.html: $(S)/dialog/dialog_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Timer test (no GL)
$(S)/timer/timer_test.o: $(S)/timer/timer_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/timer/timer_test.html: $(S)/timer/timer_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Tree test (no GL)
$(S)/tree/tree_test.o: $(S)/tree/tree_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/tree/tree_test.html: $(S)/tree/tree_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# DataView test (no GL)
$(S)/dataview/dataview_test.o: $(S)/dataview/dataview_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/dataview/dataview_test.html: $(S)/dataview/dataview_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# HtmlWindow test (needs HTML library)
$(S)/htmlwin/htmlwin_test.o: $(S)/htmlwin/htmlwin_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/htmlwin/htmlwin_test.html: $(S)/htmlwin/htmlwin_test.o
$(CXX) $< $(LDFLAGS_HTML) --pre-js $(JS) --shell-file $(HTML) -o $@
# StyledTextCtrl test (needs STC library)
$(S)/stc/stc_test.o: $(S)/stc/stc_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/stc/stc_test.html: $(S)/stc/stc_test.o
$(CXX) $< $(LDFLAGS_STC) --pre-js $(JS) --shell-file $(HTML) -o $@
# Print test (no GL)
$(S)/print/print_test.o: $(S)/print/print_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/print/print_test.html: $(S)/print/print_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# DragDrop test (no GL)
$(S)/dnd/dnd_test.o: $(S)/dnd/dnd_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/dnd/dnd_test.html: $(S)/dnd/dnd_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# PropertyGrid test (needs propgrid library)
$(S)/propgrid/propgrid_test.o: $(S)/propgrid/propgrid_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/propgrid/propgrid_test.html: $(S)/propgrid/propgrid_test.o
$(CXX) $< $(LDFLAGS_PROPGRID) --pre-js $(JS) --shell-file $(HTML) -o $@
# Pickers test (colour/font pickers, no GL)
$(S)/pickers/pickers_test.o: $(S)/pickers/pickers_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/pickers/pickers_test.html: $(S)/pickers/pickers_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Collapsible pane test (no GL)
$(S)/collapsible/collapsible_test.o: $(S)/collapsible/collapsible_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/collapsible/collapsible_test.html: $(S)/collapsible/collapsible_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# ListCtrl virtual mode test (no GL)
$(S)/listctrl/listctrl_test.o: $(S)/listctrl/listctrl_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/listctrl/listctrl_test.html: $(S)/listctrl/listctrl_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# InfoBar test (no GL)
$(S)/infobar/infobar_test.o: $(S)/infobar/infobar_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/infobar/infobar_test.html: $(S)/infobar/infobar_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# DataViewCtrl Virtual Mode test (no GL, uses base dataview)
$(S)/dataviewvirtual/dataviewvirtual_test.o: $(S)/dataviewvirtual/dataviewvirtual_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/dataviewvirtual/dataviewvirtual_test.html: $(S)/dataviewvirtual/dataviewvirtual_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# AuiNotebook test (needs aui)
$(S)/auinotebook/auinotebook_test.o: $(S)/auinotebook/auinotebook_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/auinotebook/auinotebook_test.html: $(S)/auinotebook/auinotebook_test.o
$(CXX) $< $(LDFLAGS_AUI) --pre-js $(JS) --shell-file $(HTML) -o $@
# Wizard test (needs adv)
$(S)/wizard/wizard_test.o: $(S)/wizard/wizard_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/wizard/wizard_test.html: $(S)/wizard/wizard_test.o
$(CXX) $< $(LDFLAGS_ADV) --pre-js $(JS) --shell-file $(HTML) -o $@
# Grid Edit test (cell editing, no GL)
$(S)/gridedit/gridedit_test.o: $(S)/gridedit/gridedit_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/gridedit/gridedit_test.html: $(S)/gridedit/gridedit_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Calendar test (needs adv)
$(S)/calendar/calendar_test.o: $(S)/calendar/calendar_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/calendar/calendar_test.html: $(S)/calendar/calendar_test.o
$(CXX) $< $(LDFLAGS_ADV) --pre-js $(JS) --shell-file $(HTML) -o $@
# Grid Renderers test (no GL)
$(S)/gridrenderers/gridrenderers_test.o: $(S)/gridrenderers/gridrenderers_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/gridrenderers/gridrenderers_test.html: $(S)/gridrenderers/gridrenderers_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Print Preview test (no GL)
$(S)/printpreview/printpreview_test.o: $(S)/printpreview/printpreview_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/printpreview/printpreview_test.html: $(S)/printpreview/printpreview_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Bitmap Buttons test (no GL)
$(S)/bitmapbuttons/bitmapbuttons_test.o: $(S)/bitmapbuttons/bitmapbuttons_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/bitmapbuttons/bitmapbuttons_test.html: $(S)/bitmapbuttons/bitmapbuttons_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Specialized Controls test (needs adv for treebook)
$(S)/specialized/specialized_test.o: $(S)/specialized/specialized_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/specialized/specialized_test.html: $(S)/specialized/specialized_test.o
$(CXX) $< $(LDFLAGS_ADV) --pre-js $(JS) --shell-file $(HTML) -o $@
# Validators test (no GL)
$(S)/validators/validators_test.o: $(S)/validators/validators_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/validators/validators_test.html: $(S)/validators/validators_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Owner-drawn ComboBox test (no GL)
$(S)/ownerdrawn/ownerdrawn_test.o: $(S)/ownerdrawn/ownerdrawn_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/ownerdrawn/ownerdrawn_test.html: $(S)/ownerdrawn/ownerdrawn_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Popup Window test (no GL)
$(S)/popup/popup_test.o: $(S)/popup/popup_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/popup/popup_test.html: $(S)/popup/popup_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# XML Document test (needs xml library)
$(S)/xml/xml_test.o: $(S)/xml/xml_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/xml/xml_test.html: $(S)/xml/xml_test.o
$(CXX) $< $(LDFLAGS_XML) --pre-js $(JS) --shell-file $(HTML) -o $@
# WASM Edge Cases test (no GL)
$(S)/wasmedge/wasmedge_test.o: $(S)/wasmedge/wasmedge_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/wasmedge/wasmedge_test.html: $(S)/wasmedge/wasmedge_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Font Enumeration test (no GL)
$(S)/fontenum/fontenum_test.o: $(S)/fontenum/fontenum_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/fontenum/fontenum_test.html: $(S)/fontenum/fontenum_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Text Decorations test (no GL)
$(S)/textdecor/textdecor_test.o: $(S)/textdecor/textdecor_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/textdecor/textdecor_test.html: $(S)/textdecor/textdecor_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Bitmask test (no GL)
$(S)/bitmask/bitmask_test.o: $(S)/bitmask/bitmask_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/bitmask/bitmask_test.html: $(S)/bitmask/bitmask_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Regions test (no GL)
$(S)/regions/regions_test.o: $(S)/regions/regions_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/regions/regions_test.html: $(S)/regions/regions_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Maximize test (no GL) - reproduces KiCad startup maximize issue
$(S)/maximize/maximize_test.o: $(S)/maximize/maximize_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/maximize/maximize_test.html: $(S)/maximize/maximize_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Early size test (no GL) - tests GetClientSize() before Show()
$(S)/earlysize/earlysize_test.o: $(S)/earlysize/earlysize_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/earlysize/earlysize_test.html: $(S)/earlysize/earlysize_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Thread Pool test (pthread) - reproduces KiCad deadlock when hardware_concurrency() > PTHREAD_POOL_SIZE
$(S)/threadpool/threadpool_test.o: $(S)/threadpool/threadpool_test.cpp
$(CXX) -c $(CXXFLAGS) -pthread $< -o $@
$(S)/threadpool/threadpool_test.html: $(S)/threadpool/threadpool_test.o
$(CXX) $< $(LDFLAGS_PTHREAD) --pre-js $(JS) --shell-file $(HTML) -o $@
# Log Error test (no GL) - reproduces KiCad's kiface error dialog
$(S)/logerror/logerror_test.o: $(S)/logerror/logerror_test.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
$(S)/logerror/logerror_test.html: $(S)/logerror/logerror_test.o
$(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@
# Convenience targets
menu: $(S)/menu/menu_test.html
clipboard: $(S)/clipboard/clipboard_test.html
filedialog: $(S)/filedialog/filedialog_test.html
layout: $(S)/layout/layout_test.html
aui: $(S)/aui/aui_test.html
toolbar: $(S)/toolbar/toolbar_test.html
grid: $(S)/grid/grid_test.html
dialog: $(S)/dialog/dialog_test.html
timer: $(S)/timer/timer_test.html
tree: $(S)/tree/tree_test.html
dataview: $(S)/dataview/dataview_test.html
htmlwin: $(S)/htmlwin/htmlwin_test.html
stc: $(S)/stc/stc_test.html
print: $(S)/print/print_test.html
dnd: $(S)/dnd/dnd_test.html
propgrid: $(S)/propgrid/propgrid_test.html
pickers: $(S)/pickers/pickers_test.html
collapsible: $(S)/collapsible/collapsible_test.html
listctrl: $(S)/listctrl/listctrl_test.html
infobar: $(S)/infobar/infobar_test.html
dataviewvirtual: $(S)/dataviewvirtual/dataviewvirtual_test.html
auinotebook: $(S)/auinotebook/auinotebook_test.html
wizard: $(S)/wizard/wizard_test.html
gridedit: $(S)/gridedit/gridedit_test.html
calendar: $(S)/calendar/calendar_test.html
gridrenderers: $(S)/gridrenderers/gridrenderers_test.html
printpreview: $(S)/printpreview/printpreview_test.html
bitmapbuttons: $(S)/bitmapbuttons/bitmapbuttons_test.html
specialized: $(S)/specialized/specialized_test.html
validators: $(S)/validators/validators_test.html
ownerdrawn: $(S)/ownerdrawn/ownerdrawn_test.html
popup: $(S)/popup/popup_test.html
xml: $(S)/xml/xml_test.html
wasmedge: $(S)/wasmedge/wasmedge_test.html
fontenum: $(S)/fontenum/fontenum_test.html
textdecor: $(S)/textdecor/textdecor_test.html
bitmask: $(S)/bitmask/bitmask_test.html
regions: $(S)/regions/regions_test.html
maximize: $(S)/maximize/maximize_test.html
earlysize: $(S)/earlysize/earlysize_test.html
threadpool: $(S)/threadpool/threadpool_test.html
logerror: $(S)/logerror/logerror_test.html
clean:
rm -f minimal_test.o minimal_test.html minimal_test.js minimal_test.wasm
rm -f $(S)/*/*.o $(S)/*/*.html $(S)/*/*.js $(S)/*/*.wasm
.PHONY: all clean menu clipboard filedialog layout aui toolbar grid dialog timer tree dataview htmlwin stc print dnd propgrid pickers collapsible listctrl infobar dataviewvirtual auinotebook wizard gridedit calendar gridrenderers printpreview bitmapbuttons specialized validators ownerdrawn popup xml wasmedge fontenum textdecor bitmask regions maximize earlysize threadpool logerror

View file

@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>KiCad PCBnew WASM</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; }
.window {
position: absolute;
pointer-events: none;
z-index: 10;
background-color: black;
overflow: hidden;
width: 0;
height: 0;
}
.window-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
#status {
position: fixed;
bottom: 10px;
left: 10px;
color: #fff;
font-family: monospace;
z-index: 1000;
background: rgba(0,0,0,0.7);
padding: 10px;
border-radius: 5px;
}
#progress {
width: 300px;
height: 20px;
background: #333;
margin-top: 5px;
}
#progress-bar {
height: 100%;
background: #4CAF50;
width: 0%;
transition: width 0.3s;
}
</style>
</head>
<body style="margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #1a1a2e;">
<div id="main-window" style="width: 100vw; height: 100vh; position: absolute; top: 0; left: 0;"></div>
<div id="status">
<div id="status-text">Initializing...</div>
<div id="progress"><div id="progress-bar"></div></div>
</div>
<div id="window-container"></div>
<script>
var mainWindow = document.getElementById('main-window');
var statusText = document.getElementById('status-text');
var progressBar = document.getElementById('progress-bar');
var showError = function(msg) {
console.error('[KICAD_ERROR] ' + msg);
statusText.textContent = 'Error: ' + msg;
statusText.style.color = 'red';
};
var createCanvas = function() {
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.style.display = 'none';
// Set both CSS display size and internal pixel resolution
var width = window.innerWidth;
var height = window.innerHeight;
canvas.width = width;
canvas.height = height;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
canvas.oncontextmenu = function() { event.preventDefault(); };
canvas.addEventListener("webglcontextlost", function(e) {
showError('WebGL context lost. You will need to reload the page.');
e.preventDefault();
}, false);
mainWindow.appendChild(canvas);
Module.canvas = canvas;
console.log('[KICAD] preRun complete, canvas created: ' + width + 'x' + height);
};
var onRuntimeInitialized = function() {
console.log('[KICAD] Runtime initialized');
var canvas = Module.canvas;
canvas.style.display = 'block';
document.getElementById('status').style.display = 'none';
};
var Module = {
thisProgram: '/usr/bin/pcbnew', // Fake absolute path for argv[0] (KiCad DEBUG check)
preRun: [createCanvas],
postRun: [],
print: function(text) {
if (arguments.length > 1)
text = Array.prototype.slice.call(arguments).join(' ');
console.log('[KICAD_OUT] ' + text);
},
printErr: function(text) {
if (arguments.length > 1)
text = Array.prototype.slice.call(arguments).join(' ');
console.error('[KICAD_ERR] ' + text);
},
setStatus: function(text) {
console.log('[KICAD_STATUS] ' + text);
statusText.textContent = text;
// Parse progress from status text
var match = text.match(/(\d+)\/(\d+)/);
if (match) {
var pct = (parseInt(match[1]) / parseInt(match[2])) * 100;
progressBar.style.width = pct + '%';
}
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
},
onRuntimeInitialized: onRuntimeInitialized,
// Required for locating .wasm and .worker.js files
locateFile: function(path) {
return path;
}
};
Module.setStatus('Downloading...');
window.onerror = function(msg, url, line) {
showError(msg + ' at ' + url + ':' + line);
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
return false;
};
</script>
<!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
<script src="wx.js"></script>
<script async src="pcbnew.js"></script>
</body>
</html>

1490
tests/apps/minimal_test.cpp Normal file

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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