Reorganize project structure for clarity
- Rename tests/wasm-app/ to tests/apps/ for brevity - Move lib/gl_immediate_shim.js to wasm/shims/ (consolidates WASM files) - Delete docs/ directory (outdated Nov 2024 research docs) - Delete patches/ directory (already applied to fork submodules) - Delete wasm/patches/ and wasm/config/ (empty/unused) - Update all file references in scripts, configs, and documentation - Update .gitignore for new tests/apps/ paths Verified both build workflows pass: - wxWidgets tests: 255 passed - Docker KiCad build: completed successfully 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c4cd69f28d
commit
a26b40c063
78 changed files with 38 additions and 128055 deletions
495
tests/apps/Makefile.wasm
Normal file
495
tests/apps/Makefile.wasm
Normal 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
|
||||
166
tests/apps/kicad/pcbnew.html
Normal file
166
tests/apps/kicad/pcbnew.html
Normal 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
1490
tests/apps/minimal_test.cpp
Normal file
File diff suppressed because it is too large
Load diff
143
tests/apps/standalone/aui/aui_test.cpp
Normal file
143
tests/apps/standalone/aui/aui_test.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// wxAuiManager Test - Tests AUI docking functionality in WASM
|
||||
// KiCad uses AUI extensively for dockable panels
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/aui/aui.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class AuiTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class AuiTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
AuiTestFrame();
|
||||
~AuiTestFrame();
|
||||
|
||||
private:
|
||||
wxAuiManager m_mgr;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void OnPaneClose(wxAuiManagerEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(AuiTestFrame, wxFrame)
|
||||
EVT_AUI_PANE_CLOSE(AuiTestFrame::OnPaneClose)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(AuiTestApp);
|
||||
|
||||
bool AuiTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
AuiTestFrame* frame = new AuiTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
AuiTestFrame::AuiTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxAuiManager WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
m_mgr.SetManagedWindow(this);
|
||||
|
||||
// Create center pane with event log
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
m_mgr.AddPane(m_log, wxAuiPaneInfo().Name("log").Caption("Event Log")
|
||||
.Center().CloseButton(false));
|
||||
|
||||
// Create left panel (like KiCad's properties panel)
|
||||
wxPanel* leftPanel = new wxPanel(this);
|
||||
leftPanel->SetBackgroundColour(*wxLIGHT_GREY);
|
||||
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
|
||||
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "Properties Panel"), 0, wxALL, 10);
|
||||
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "Like KiCad's"), 0, wxALL, 5);
|
||||
leftSizer->Add(new wxStaticText(leftPanel, wxID_ANY, "property editor"), 0, wxALL, 5);
|
||||
leftPanel->SetSizer(leftSizer);
|
||||
|
||||
m_mgr.AddPane(leftPanel, wxAuiPaneInfo().Name("properties").Caption("Properties")
|
||||
.Left().Layer(1).Position(1).CloseButton(true).MaximizeButton(true)
|
||||
.MinSize(150, 200));
|
||||
|
||||
// Create right panel (like KiCad's layer manager)
|
||||
wxPanel* rightPanel = new wxPanel(this);
|
||||
rightPanel->SetBackgroundColour(wxColour(240, 240, 255));
|
||||
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
|
||||
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "Layers Panel"), 0, wxALL, 10);
|
||||
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "Like KiCad's"), 0, wxALL, 5);
|
||||
rightSizer->Add(new wxStaticText(rightPanel, wxID_ANY, "layer manager"), 0, wxALL, 5);
|
||||
rightPanel->SetSizer(rightSizer);
|
||||
|
||||
m_mgr.AddPane(rightPanel, wxAuiPaneInfo().Name("layers").Caption("Layers")
|
||||
.Right().Layer(1).Position(1).CloseButton(true).MaximizeButton(true)
|
||||
.MinSize(150, 200));
|
||||
|
||||
// Create bottom panel (like KiCad's message panel)
|
||||
wxPanel* bottomPanel = new wxPanel(this);
|
||||
bottomPanel->SetBackgroundColour(wxColour(255, 255, 240));
|
||||
wxBoxSizer* bottomSizer = new wxBoxSizer(wxVERTICAL);
|
||||
bottomSizer->Add(new wxStaticText(bottomPanel, wxID_ANY,
|
||||
"Message Panel - Like KiCad's message area"), 0, wxALL, 5);
|
||||
bottomPanel->SetSizer(bottomSizer);
|
||||
|
||||
m_mgr.AddPane(bottomPanel, wxAuiPaneInfo().Name("messages").Caption("Messages")
|
||||
.Bottom().Layer(0).Position(1).CloseButton(true)
|
||||
.MinSize(-1, 80));
|
||||
|
||||
m_mgr.Update();
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("AUI Manager ready - try dragging and docking panels");
|
||||
|
||||
LogEvent("AUI test app started");
|
||||
LogEvent("Created dockable panels: Properties, Layers, Messages");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[AUI_TEST] wxAuiManager test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
AuiTestFrame::~AuiTestFrame()
|
||||
{
|
||||
m_mgr.UnInit();
|
||||
}
|
||||
|
||||
void AuiTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[AUI_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void AuiTestFrame::OnPaneClose(wxAuiManagerEvent& evt)
|
||||
{
|
||||
wxAuiPaneInfo* pane = evt.GetPane();
|
||||
if (pane) {
|
||||
LogEvent(wxString::Format("Pane closing: %s", pane->name));
|
||||
}
|
||||
}
|
||||
244
tests/apps/standalone/auinotebook/auinotebook_test.cpp
Normal file
244
tests/apps/standalone/auinotebook/auinotebook_test.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
// wxAuiNotebook Test - Tab panels for KiCad editors
|
||||
// Tests wxAuiNotebook for dockable, closeable tab panels
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/aui/auibook.h"
|
||||
#include "wx/aui/aui.h"
|
||||
#include "wx/textctrl.h"
|
||||
|
||||
class AuiNotebookFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
AuiNotebookFrame() : wxFrame(nullptr, wxID_ANY, "wxAuiNotebook Test",
|
||||
wxDefaultPosition, wxSize(1000, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxAuiNotebook for tabbed panels in some editors.\n"
|
||||
"Features: closeable tabs, reorderable tabs, split views.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Controls
|
||||
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btnAdd = new wxButton(mainPanel, wxID_ANY, "Add Tab");
|
||||
wxButton* btnRemove = new wxButton(mainPanel, wxID_ANY, "Remove Tab");
|
||||
wxButton* btnSplit = new wxButton(mainPanel, wxID_ANY, "Split View");
|
||||
|
||||
btnAdd->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnAddTab, this);
|
||||
btnRemove->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnRemoveTab, this);
|
||||
btnSplit->Bind(wxEVT_BUTTON, &AuiNotebookFrame::OnSplitView, this);
|
||||
|
||||
controlSizer->Add(btnAdd, 0, wxRIGHT, 5);
|
||||
controlSizer->Add(btnRemove, 0, wxRIGHT, 5);
|
||||
controlSizer->Add(btnSplit, 0, wxRIGHT, 20);
|
||||
|
||||
// Style options
|
||||
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Tab Style:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
|
||||
|
||||
wxButton* btnTop = new wxButton(mainPanel, wxID_ANY, "Top");
|
||||
wxButton* btnBottom = new wxButton(mainPanel, wxID_ANY, "Bottom");
|
||||
|
||||
btnTop->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetTabPosition(wxAUI_NB_TOP); });
|
||||
btnBottom->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetTabPosition(wxAUI_NB_BOTTOM); });
|
||||
|
||||
controlSizer->Add(btnTop, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btnBottom, 0);
|
||||
|
||||
mainSizer->Add(controlSizer, 0, wxALL, 5);
|
||||
|
||||
// AuiNotebook
|
||||
m_notebook = new wxAuiNotebook(mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
||||
wxAUI_NB_DEFAULT_STYLE | wxAUI_NB_TAB_EXTERNAL_MOVE | wxAUI_NB_CLOSE_ON_ALL_TABS);
|
||||
|
||||
// Create initial tabs like KiCad editor panels
|
||||
CreateSchematicTab();
|
||||
CreatePCBTab();
|
||||
CreateSymbolTab();
|
||||
CreateFootprintTab();
|
||||
|
||||
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Bind notebook events
|
||||
m_notebook->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, &AuiNotebookFrame::OnPageChanged, this);
|
||||
m_notebook->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, &AuiNotebookFrame::OnPageClose, this);
|
||||
m_notebook->Bind(wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN, &AuiNotebookFrame::OnTabRightClick, this);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("AuiNotebook test app started");
|
||||
|
||||
Log("AuiNotebook test app started");
|
||||
Log("4 tabs created: Schematic, PCB, Symbol, Footprint");
|
||||
|
||||
m_tabCounter = 5;
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSchematicTab()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "Schematic Editor"), 0, wxALL, 10);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Schematic Editor tab."), 0, wxLEFT, 10);
|
||||
|
||||
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
|
||||
"Components:\n- U1: STM32F103\n- R1-R10: 10k Resistors\n- C1-C5: 100nF Capacitors\n- J1: USB Connector",
|
||||
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Schematic", true);
|
||||
}
|
||||
|
||||
void CreatePCBTab()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "PCB Editor"), 0, wxALL, 10);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad PCB Editor tab."), 0, wxLEFT, 10);
|
||||
|
||||
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
|
||||
"Board Info:\n- Size: 100mm x 80mm\n- Layers: 4 (F.Cu, In1.Cu, In2.Cu, B.Cu)\n- Track Width: 0.25mm\n- Via Size: 0.8mm",
|
||||
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "PCB", false);
|
||||
}
|
||||
|
||||
void CreateSymbolTab()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "Symbol Editor"), 0, wxALL, 10);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Symbol Editor tab."), 0, wxLEFT, 10);
|
||||
|
||||
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
|
||||
"Symbol: STM32F103\n- Pins: 48\n- Units: 4 (A, B, C, D)\n- Power Pins: VCC, GND\n- Library: MCU_ST_STM32",
|
||||
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Symbol", false);
|
||||
}
|
||||
|
||||
void CreateFootprintTab()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "Footprint Editor"), 0, wxALL, 10);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "This simulates the KiCad Footprint Editor tab."), 0, wxLEFT, 10);
|
||||
|
||||
wxTextCtrl* content = new wxTextCtrl(panel, wxID_ANY,
|
||||
"Footprint: LQFP-48\n- Pads: 48\n- Pitch: 0.5mm\n- Package: 7x7mm\n- Library: Package_QFP",
|
||||
wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(content, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Footprint", false);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void OnAddTab(wxCommandEvent& event)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxString tabName = wxString::Format("Tab %d", m_tabCounter++);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, tabName), 0, wxALL, 10);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, "New dynamically created tab"), 0, wxLEFT, 10);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, tabName, true);
|
||||
|
||||
Log(wxString::Format("Added new tab: %s", tabName));
|
||||
}
|
||||
|
||||
void OnRemoveTab(wxCommandEvent& event)
|
||||
{
|
||||
int selection = m_notebook->GetSelection();
|
||||
if (selection != wxNOT_FOUND && m_notebook->GetPageCount() > 1)
|
||||
{
|
||||
wxString tabName = m_notebook->GetPageText(selection);
|
||||
m_notebook->DeletePage(selection);
|
||||
Log(wxString::Format("Removed tab: %s", tabName));
|
||||
}
|
||||
}
|
||||
|
||||
void OnSplitView(wxCommandEvent& event)
|
||||
{
|
||||
// wxAuiNotebook supports split views natively
|
||||
Log("Split view requested (drag tab to edge to split)");
|
||||
}
|
||||
|
||||
void SetTabPosition(int style)
|
||||
{
|
||||
long currentStyle = m_notebook->GetWindowStyleFlag();
|
||||
currentStyle &= ~(wxAUI_NB_TOP | wxAUI_NB_BOTTOM);
|
||||
currentStyle |= style;
|
||||
m_notebook->SetWindowStyleFlag(currentStyle);
|
||||
m_notebook->Refresh();
|
||||
|
||||
Log(style == wxAUI_NB_TOP ? "Tabs moved to top" : "Tabs moved to bottom");
|
||||
}
|
||||
|
||||
void OnPageChanged(wxAuiNotebookEvent& event)
|
||||
{
|
||||
int sel = event.GetSelection();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
Log(wxString::Format("Tab changed to: %s", m_notebook->GetPageText(sel)));
|
||||
}
|
||||
}
|
||||
|
||||
void OnPageClose(wxAuiNotebookEvent& event)
|
||||
{
|
||||
int sel = event.GetSelection();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
Log(wxString::Format("Tab closing: %s", m_notebook->GetPageText(sel)));
|
||||
}
|
||||
}
|
||||
|
||||
void OnTabRightClick(wxAuiNotebookEvent& event)
|
||||
{
|
||||
Log("Tab right-clicked (context menu would appear)");
|
||||
}
|
||||
|
||||
wxAuiNotebook* m_notebook;
|
||||
wxTextCtrl* m_log;
|
||||
int m_tabCounter;
|
||||
};
|
||||
|
||||
class AuiNotebookApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
AuiNotebookFrame* frame = new AuiNotebookFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(AuiNotebookApp);
|
||||
280
tests/apps/standalone/bitmapbuttons/bitmapbuttons_test.cpp
Normal file
280
tests/apps/standalone/bitmapbuttons/bitmapbuttons_test.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
// wxBitmapButton Test - Custom bitmap buttons like KiCad's toolbar
|
||||
// Tests wxBitmapButton, disabled states, different icon shapes
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/dcmemory.h"
|
||||
#include "wx/artprov.h"
|
||||
|
||||
// Helper to create simple bitmap icons
|
||||
wxBitmap CreateIcon(const wxColour& color, int size = 24, const wxString& shape = "rect")
|
||||
{
|
||||
wxBitmap bmp(size, size);
|
||||
wxMemoryDC dc(bmp);
|
||||
|
||||
dc.SetBackground(wxBrush(wxColour(240, 240, 240)));
|
||||
dc.Clear();
|
||||
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(wxBrush(color));
|
||||
|
||||
if (shape == "circle")
|
||||
{
|
||||
dc.DrawCircle(size / 2, size / 2, size / 2 - 2);
|
||||
}
|
||||
else if (shape == "triangle")
|
||||
{
|
||||
wxPoint points[3] = {
|
||||
wxPoint(size / 2, 2),
|
||||
wxPoint(2, size - 2),
|
||||
wxPoint(size - 2, size - 2)
|
||||
};
|
||||
dc.DrawPolygon(3, points);
|
||||
}
|
||||
else if (shape == "diamond")
|
||||
{
|
||||
wxPoint points[4] = {
|
||||
wxPoint(size / 2, 2),
|
||||
wxPoint(2, size / 2),
|
||||
wxPoint(size / 2, size - 2),
|
||||
wxPoint(size - 2, size / 2)
|
||||
};
|
||||
dc.DrawPolygon(4, points);
|
||||
}
|
||||
else // rect
|
||||
{
|
||||
dc.DrawRectangle(2, 2, size - 4, size - 4);
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
// Create a "tool" icon with multiple elements
|
||||
wxBitmap CreateToolIcon(const wxColour& mainColor, const wxString& symbol)
|
||||
{
|
||||
int size = 24;
|
||||
wxBitmap bmp(size, size);
|
||||
wxMemoryDC dc(bmp);
|
||||
|
||||
dc.SetBackground(wxBrush(wxColour(240, 240, 240)));
|
||||
dc.Clear();
|
||||
|
||||
// Draw main shape
|
||||
dc.SetPen(wxPen(mainColor, 2));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRectangle(3, 3, 18, 18);
|
||||
|
||||
// Draw symbol
|
||||
dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc.SetTextForeground(mainColor);
|
||||
|
||||
wxSize textSize = dc.GetTextExtent(symbol);
|
||||
dc.DrawText(symbol, (size - textSize.x) / 2, (size - textSize.y) / 2);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
class BitmapButtonsFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
BitmapButtonsFrame() : wxFrame(nullptr, wxID_ANY, "wxBitmapButton Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxBitmapButton extensively for toolbars and dialogs.\n"
|
||||
"Tests: Bitmap buttons, disabled states, icon buttons.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Toolbar-style buttons
|
||||
wxStaticBoxSizer* toolbarSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Toolbar Style");
|
||||
|
||||
m_btnSelect = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxBLACK, "S"));
|
||||
m_btnSelect->SetToolTip("Select Tool");
|
||||
|
||||
m_btnLine = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxBLUE, "L"));
|
||||
m_btnLine->SetToolTip("Line Tool");
|
||||
|
||||
m_btnRect = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(wxColour(0, 128, 0), "R"));
|
||||
m_btnRect->SetToolTip("Rectangle Tool");
|
||||
|
||||
m_btnCircle = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(*wxRED, "C"));
|
||||
m_btnCircle->SetToolTip("Circle Tool");
|
||||
|
||||
m_btnText = new wxBitmapButton(mainPanel, wxID_ANY, CreateToolIcon(wxColour(128, 0, 128), "T"));
|
||||
m_btnText->SetToolTip("Text Tool");
|
||||
|
||||
m_btnSelect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Select tool clicked"); });
|
||||
m_btnLine->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Line tool clicked"); });
|
||||
m_btnRect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Rectangle tool clicked"); });
|
||||
m_btnCircle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Circle tool clicked"); });
|
||||
m_btnText->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Text tool clicked"); });
|
||||
|
||||
toolbarSizer->Add(m_btnSelect, 0, wxALL, 2);
|
||||
toolbarSizer->Add(m_btnLine, 0, wxALL, 2);
|
||||
toolbarSizer->Add(m_btnRect, 0, wxALL, 2);
|
||||
toolbarSizer->Add(m_btnCircle, 0, wxALL, 2);
|
||||
toolbarSizer->Add(m_btnText, 0, wxALL, 2);
|
||||
|
||||
mainSizer->Add(toolbarSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Layer visibility buttons (simulated with checkboxes)
|
||||
wxStaticBoxSizer* layerSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Layer Visibility (Toggle Buttons)");
|
||||
|
||||
m_chkFCu = new wxCheckBox(mainPanel, wxID_ANY, "F.Cu");
|
||||
m_chkFCu->SetValue(true);
|
||||
m_chkBCu = new wxCheckBox(mainPanel, wxID_ANY, "B.Cu");
|
||||
m_chkBCu->SetValue(true);
|
||||
m_chkSilk = new wxCheckBox(mainPanel, wxID_ANY, "Silk");
|
||||
m_chkSilk->SetValue(true);
|
||||
m_chkMask = new wxCheckBox(mainPanel, wxID_ANY, "Mask");
|
||||
m_chkMask->SetValue(false);
|
||||
|
||||
m_chkFCu->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
|
||||
Log(wxString::Format("F.Cu toggle: %s", e.IsChecked() ? "ON" : "OFF"));
|
||||
});
|
||||
m_chkBCu->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
|
||||
Log(wxString::Format("B.Cu toggle: %s", e.IsChecked() ? "ON" : "OFF"));
|
||||
});
|
||||
m_chkSilk->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
|
||||
Log(wxString::Format("Silk toggle: %s", e.IsChecked() ? "ON" : "OFF"));
|
||||
});
|
||||
m_chkMask->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& e) {
|
||||
Log(wxString::Format("Mask toggle: %s", e.IsChecked() ? "ON" : "OFF"));
|
||||
});
|
||||
|
||||
layerSizer->Add(m_chkFCu, 0, wxALL, 5);
|
||||
layerSizer->Add(m_chkBCu, 0, wxALL, 5);
|
||||
layerSizer->Add(m_chkSilk, 0, wxALL, 5);
|
||||
layerSizer->Add(m_chkMask, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(layerSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Disabled state buttons
|
||||
wxStaticBoxSizer* disabledSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Disabled State");
|
||||
|
||||
m_btnEnabled = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(0, 128, 0), 24, "circle"));
|
||||
m_btnEnabled->SetToolTip("Enabled button");
|
||||
|
||||
m_btnDisabled = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(128, 128, 128), 24, "circle"));
|
||||
m_btnDisabled->Enable(false);
|
||||
m_btnDisabled->SetToolTip("Disabled button");
|
||||
|
||||
wxButton* btnToggleEnabled = new wxButton(mainPanel, wxID_ANY, "Toggle Enable State");
|
||||
btnToggleEnabled->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
m_btnDisabled->Enable(!m_btnDisabled->IsEnabled());
|
||||
Log(wxString::Format("Button enabled: %s", m_btnDisabled->IsEnabled() ? "Yes" : "No"));
|
||||
});
|
||||
|
||||
disabledSizer->Add(m_btnEnabled, 0, wxALL, 5);
|
||||
disabledSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Enabled"), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
disabledSizer->AddSpacer(20);
|
||||
disabledSizer->Add(m_btnDisabled, 0, wxALL, 5);
|
||||
disabledSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Disabled"), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
disabledSizer->AddSpacer(20);
|
||||
disabledSizer->Add(btnToggleEnabled, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(disabledSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Different shapes
|
||||
wxStaticBoxSizer* shapesSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Different Icon Shapes");
|
||||
|
||||
wxBitmapButton* btnRect = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(*wxRED, 32, "rect"));
|
||||
wxBitmapButton* btnCircle = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(*wxBLUE, 32, "circle"));
|
||||
wxBitmapButton* btnTriangle = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(0, 128, 0), 32, "triangle"));
|
||||
wxBitmapButton* btnDiamond = new wxBitmapButton(mainPanel, wxID_ANY, CreateIcon(wxColour(128, 0, 128), 32, "diamond"));
|
||||
|
||||
btnRect->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Rectangle shape clicked"); });
|
||||
btnCircle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Circle shape clicked"); });
|
||||
btnTriangle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Triangle shape clicked"); });
|
||||
btnDiamond->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Diamond shape clicked"); });
|
||||
|
||||
shapesSizer->Add(btnRect, 0, wxALL, 5);
|
||||
shapesSizer->Add(btnCircle, 0, wxALL, 5);
|
||||
shapesSizer->Add(btnTriangle, 0, wxALL, 5);
|
||||
shapesSizer->Add(btnDiamond, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(shapesSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Art Provider buttons
|
||||
wxStaticBoxSizer* artSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Art Provider Icons");
|
||||
|
||||
wxBitmapButton* btnNew = new wxBitmapButton(mainPanel, wxID_ANY,
|
||||
wxArtProvider::GetBitmap(wxART_NEW, wxART_TOOLBAR));
|
||||
wxBitmapButton* btnOpen = new wxBitmapButton(mainPanel, wxID_ANY,
|
||||
wxArtProvider::GetBitmap(wxART_FILE_OPEN, wxART_TOOLBAR));
|
||||
wxBitmapButton* btnSave = new wxBitmapButton(mainPanel, wxID_ANY,
|
||||
wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_TOOLBAR));
|
||||
wxBitmapButton* btnUndo = new wxBitmapButton(mainPanel, wxID_ANY,
|
||||
wxArtProvider::GetBitmap(wxART_UNDO, wxART_TOOLBAR));
|
||||
wxBitmapButton* btnRedo = new wxBitmapButton(mainPanel, wxID_ANY,
|
||||
wxArtProvider::GetBitmap(wxART_REDO, wxART_TOOLBAR));
|
||||
|
||||
btnNew->SetToolTip("New");
|
||||
btnOpen->SetToolTip("Open");
|
||||
btnSave->SetToolTip("Save");
|
||||
btnUndo->SetToolTip("Undo");
|
||||
btnRedo->SetToolTip("Redo");
|
||||
|
||||
btnNew->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("New clicked"); });
|
||||
btnOpen->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Open clicked"); });
|
||||
btnSave->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Save clicked"); });
|
||||
btnUndo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Undo clicked"); });
|
||||
btnRedo->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Log("Redo clicked"); });
|
||||
|
||||
artSizer->Add(btnNew, 0, wxALL, 2);
|
||||
artSizer->Add(btnOpen, 0, wxALL, 2);
|
||||
artSizer->Add(btnSave, 0, wxALL, 2);
|
||||
artSizer->AddSpacer(10);
|
||||
artSizer->Add(btnUndo, 0, wxALL, 2);
|
||||
artSizer->Add(btnRedo, 0, wxALL, 2);
|
||||
|
||||
mainSizer->Add(artSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Bitmap buttons test app started");
|
||||
Log("Bitmap buttons test app started");
|
||||
}
|
||||
|
||||
private:
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxBitmapButton* m_btnSelect;
|
||||
wxBitmapButton* m_btnLine;
|
||||
wxBitmapButton* m_btnRect;
|
||||
wxBitmapButton* m_btnCircle;
|
||||
wxBitmapButton* m_btnText;
|
||||
wxCheckBox* m_chkFCu;
|
||||
wxCheckBox* m_chkBCu;
|
||||
wxCheckBox* m_chkSilk;
|
||||
wxCheckBox* m_chkMask;
|
||||
wxBitmapButton* m_btnEnabled;
|
||||
wxBitmapButton* m_btnDisabled;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class BitmapButtonsApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
BitmapButtonsFrame* frame = new BitmapButtonsFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(BitmapButtonsApp);
|
||||
206
tests/apps/standalone/bitmask/bitmask_test.cpp
Normal file
206
tests/apps/standalone/bitmask/bitmask_test.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
// Bitmap Masking Test - Tests wxBitmap with wxMask for transparency
|
||||
// Tests: wxMask, wxBitmap::SetMask(), wxDC::DrawBitmap() with useMask=true
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/dcmemory.h"
|
||||
|
||||
class BitmaskFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
BitmaskFrame() : wxFrame(nullptr, wxID_ANY, "Bitmap Masking Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"Tests bitmap masking for transparency.\n"
|
||||
"Uses wxMask with color-keyed transparency.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Drawing panel
|
||||
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 450));
|
||||
m_drawPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_drawPanel->Bind(wxEVT_PAINT, &BitmaskFrame::OnPaint, this);
|
||||
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Create test bitmaps
|
||||
CreateTestBitmaps();
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Bitmap masking test app started");
|
||||
Log("Bitmap masking test app started");
|
||||
Log("Testing wxMask with color-keyed transparency");
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateTestBitmaps()
|
||||
{
|
||||
// Create a bitmap with magenta (255,0,255) as transparent color
|
||||
m_bitmapWithMask.Create(80, 80, 32);
|
||||
wxMemoryDC dc(m_bitmapWithMask);
|
||||
|
||||
// Fill with magenta (will become transparent)
|
||||
dc.SetBackground(wxBrush(wxColour(255, 0, 255)));
|
||||
dc.Clear();
|
||||
|
||||
// Draw a blue circle in the center
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 255)));
|
||||
dc.SetPen(wxPen(wxColour(0, 0, 128), 2));
|
||||
dc.DrawCircle(40, 40, 30);
|
||||
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
|
||||
// Set the mask using magenta as transparent color
|
||||
m_bitmapWithMask.SetMask(new wxMask(m_bitmapWithMask, wxColour(255, 0, 255)));
|
||||
Log("Created 80x80 bitmap with magenta mask");
|
||||
|
||||
// Create a bitmap with green as transparent color
|
||||
m_bitmapGreenMask.Create(80, 80, 32);
|
||||
wxMemoryDC dc2(m_bitmapGreenMask);
|
||||
|
||||
// Fill with green (will become transparent)
|
||||
dc2.SetBackground(wxBrush(wxColour(0, 255, 0)));
|
||||
dc2.Clear();
|
||||
|
||||
// Draw a red rectangle
|
||||
dc2.SetBrush(wxBrush(wxColour(255, 0, 0)));
|
||||
dc2.SetPen(wxPen(wxColour(128, 0, 0), 2));
|
||||
dc2.DrawRectangle(15, 15, 50, 50);
|
||||
|
||||
dc2.SelectObject(wxNullBitmap);
|
||||
|
||||
m_bitmapGreenMask.SetMask(new wxMask(m_bitmapGreenMask, wxColour(0, 255, 0)));
|
||||
Log("Created 80x80 bitmap with green mask");
|
||||
|
||||
// Create a bitmap without mask for comparison
|
||||
m_bitmapNoMask.Create(80, 80, 32);
|
||||
wxMemoryDC dc3(m_bitmapNoMask);
|
||||
|
||||
dc3.SetBackground(wxBrush(wxColour(255, 0, 255)));
|
||||
dc3.Clear();
|
||||
|
||||
dc3.SetBrush(wxBrush(wxColour(0, 0, 255)));
|
||||
dc3.SetPen(wxPen(wxColour(0, 0, 128), 2));
|
||||
dc3.DrawCircle(40, 40, 30);
|
||||
|
||||
dc3.SelectObject(wxNullBitmap);
|
||||
Log("Created 80x80 bitmap without mask");
|
||||
|
||||
// Create a checkerboard pattern bitmap with mask
|
||||
m_bitmapCheckerboard.Create(80, 80, 32);
|
||||
wxMemoryDC dc4(m_bitmapCheckerboard);
|
||||
|
||||
dc4.SetBackground(wxBrush(wxColour(255, 0, 255)));
|
||||
dc4.Clear();
|
||||
|
||||
// Draw checkerboard
|
||||
for (int y = 0; y < 80; y += 20)
|
||||
{
|
||||
for (int x = 0; x < 80; x += 20)
|
||||
{
|
||||
if ((x / 20 + y / 20) % 2 == 0)
|
||||
{
|
||||
dc4.SetBrush(wxBrush(wxColour(50, 50, 50)));
|
||||
dc4.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc4.DrawRectangle(x, y, 20, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dc4.SelectObject(wxNullBitmap);
|
||||
m_bitmapCheckerboard.SetMask(new wxMask(m_bitmapCheckerboard, wxColour(255, 0, 255)));
|
||||
Log("Created 80x80 checkerboard bitmap with mask");
|
||||
}
|
||||
|
||||
void OnPaint(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_drawPanel);
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
int y = 20;
|
||||
|
||||
// Section 1: Show bitmap without mask
|
||||
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc.DrawText("No Mask (magenta visible):", 20, y);
|
||||
dc.DrawBitmap(m_bitmapNoMask, 300, y - 10, false);
|
||||
y += 100;
|
||||
|
||||
// Section 2: Show bitmap with mask
|
||||
dc.DrawText("With Mask (magenta transparent):", 20, y);
|
||||
dc.DrawBitmap(m_bitmapWithMask, 300, y - 10, true);
|
||||
y += 100;
|
||||
|
||||
// Section 3: Show green mask bitmap
|
||||
dc.DrawText("Green Mask (green transparent):", 20, y);
|
||||
dc.DrawBitmap(m_bitmapGreenMask, 300, y - 10, true);
|
||||
y += 100;
|
||||
|
||||
// Section 4: Show on colored background to verify transparency
|
||||
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc.DrawText("Masked bitmaps on colored backgrounds:", 20, y);
|
||||
y += 25;
|
||||
|
||||
// Yellow background
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 0)));
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.DrawRectangle(100, y, 100, 100);
|
||||
dc.DrawBitmap(m_bitmapWithMask, 110, y + 10, true);
|
||||
|
||||
// Cyan background
|
||||
dc.SetBrush(wxBrush(wxColour(0, 255, 255)));
|
||||
dc.DrawRectangle(220, y, 100, 100);
|
||||
dc.DrawBitmap(m_bitmapGreenMask, 230, y + 10, true);
|
||||
|
||||
// Gray background with checkerboard
|
||||
dc.SetBrush(wxBrush(wxColour(200, 200, 200)));
|
||||
dc.DrawRectangle(340, y, 100, 100);
|
||||
dc.DrawBitmap(m_bitmapCheckerboard, 350, y + 10, true);
|
||||
|
||||
// Gradient-like striped background
|
||||
for (int i = 0; i < 100; i += 10)
|
||||
{
|
||||
dc.SetBrush(wxBrush(wxColour(100 + i, 50, 150 - i)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(460 + i, y, 10, 100);
|
||||
}
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.DrawRectangle(460, y, 100, 100);
|
||||
dc.DrawBitmap(m_bitmapWithMask, 470, y + 10, true);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxPanel* m_drawPanel;
|
||||
wxTextCtrl* m_log;
|
||||
wxBitmap m_bitmapWithMask;
|
||||
wxBitmap m_bitmapNoMask;
|
||||
wxBitmap m_bitmapGreenMask;
|
||||
wxBitmap m_bitmapCheckerboard;
|
||||
};
|
||||
|
||||
class BitmaskApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
BitmaskFrame* frame = new BitmaskFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(BitmaskApp);
|
||||
213
tests/apps/standalone/calendar/calendar_test.cpp
Normal file
213
tests/apps/standalone/calendar/calendar_test.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// wxCalendarCtrl Test - Date selection
|
||||
// Tests wxCalendarCtrl for date picking functionality
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/calctrl.h"
|
||||
#include "wx/datectrl.h"
|
||||
#include "wx/dateevt.h"
|
||||
|
||||
class CalendarFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
CalendarFrame() : wxFrame(nullptr, wxID_ANY, "wxCalendarCtrl Test",
|
||||
wxDefaultPosition, wxSize(700, 550))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad may use wxCalendarCtrl for date-related features.\n"
|
||||
"This tests calendar widget rendering and date selection.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Horizontal layout for calendar and controls
|
||||
wxBoxSizer* hSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Calendar control
|
||||
wxStaticBoxSizer* calBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Calendar");
|
||||
m_calendar = new wxCalendarCtrl(mainPanel, wxID_ANY, wxDefaultDateTime,
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxCAL_SHOW_HOLIDAYS | wxCAL_SHOW_SURROUNDING_WEEKS);
|
||||
calBox->Add(m_calendar, 1, wxEXPAND | wxALL, 5);
|
||||
hSizer->Add(calBox, 1, wxEXPAND | wxRIGHT, 10);
|
||||
|
||||
// Controls panel
|
||||
wxBoxSizer* controlSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Date picker
|
||||
wxStaticBoxSizer* pickerBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Date Picker");
|
||||
m_datePicker = new wxDatePickerCtrl(mainPanel, wxID_ANY);
|
||||
pickerBox->Add(m_datePicker, 0, wxEXPAND | wxALL, 5);
|
||||
controlSizer->Add(pickerBox, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
// Navigation buttons
|
||||
wxStaticBoxSizer* navBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Navigation");
|
||||
|
||||
wxButton* btnToday = new wxButton(mainPanel, wxID_ANY, "Today");
|
||||
wxButton* btnPrevMonth = new wxButton(mainPanel, wxID_ANY, "Previous Month");
|
||||
wxButton* btnNextMonth = new wxButton(mainPanel, wxID_ANY, "Next Month");
|
||||
wxButton* btnPrevYear = new wxButton(mainPanel, wxID_ANY, "Previous Year");
|
||||
wxButton* btnNextYear = new wxButton(mainPanel, wxID_ANY, "Next Year");
|
||||
|
||||
btnToday->Bind(wxEVT_BUTTON, &CalendarFrame::OnToday, this);
|
||||
btnPrevMonth->Bind(wxEVT_BUTTON, &CalendarFrame::OnPrevMonth, this);
|
||||
btnNextMonth->Bind(wxEVT_BUTTON, &CalendarFrame::OnNextMonth, this);
|
||||
btnPrevYear->Bind(wxEVT_BUTTON, &CalendarFrame::OnPrevYear, this);
|
||||
btnNextYear->Bind(wxEVT_BUTTON, &CalendarFrame::OnNextYear, this);
|
||||
|
||||
navBox->Add(btnToday, 0, wxEXPAND | wxALL, 2);
|
||||
navBox->Add(btnPrevMonth, 0, wxEXPAND | wxALL, 2);
|
||||
navBox->Add(btnNextMonth, 0, wxEXPAND | wxALL, 2);
|
||||
navBox->Add(btnPrevYear, 0, wxEXPAND | wxALL, 2);
|
||||
navBox->Add(btnNextYear, 0, wxEXPAND | wxALL, 2);
|
||||
|
||||
controlSizer->Add(navBox, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
// Selected date display
|
||||
wxStaticBoxSizer* selBox = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Selected Date");
|
||||
m_selectedLabel = new wxStaticText(mainPanel, wxID_ANY, "No date selected");
|
||||
selBox->Add(m_selectedLabel, 0, wxALL, 5);
|
||||
controlSizer->Add(selBox, 0, wxEXPAND);
|
||||
|
||||
hSizer->Add(controlSizer, 0, wxEXPAND);
|
||||
mainSizer->Add(hSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 120),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Bind calendar events
|
||||
m_calendar->Bind(wxEVT_CALENDAR_SEL_CHANGED, &CalendarFrame::OnDateChanged, this);
|
||||
m_calendar->Bind(wxEVT_CALENDAR_DAY_CHANGED, &CalendarFrame::OnDayChanged, this);
|
||||
m_calendar->Bind(wxEVT_CALENDAR_MONTH_CHANGED, &CalendarFrame::OnMonthChanged, this);
|
||||
m_calendar->Bind(wxEVT_CALENDAR_YEAR_CHANGED, &CalendarFrame::OnYearChanged, this);
|
||||
m_calendar->Bind(wxEVT_CALENDAR_DOUBLECLICKED, &CalendarFrame::OnDoubleClicked, this);
|
||||
|
||||
m_datePicker->Bind(wxEVT_DATE_CHANGED, &CalendarFrame::OnPickerChanged, this);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("Calendar test app started");
|
||||
|
||||
Log("Calendar test app started");
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
private:
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void UpdateSelectedLabel()
|
||||
{
|
||||
wxDateTime date = m_calendar->GetDate();
|
||||
m_selectedLabel->SetLabel(date.FormatDate());
|
||||
}
|
||||
|
||||
void OnDateChanged(wxCalendarEvent& event)
|
||||
{
|
||||
wxDateTime date = event.GetDate();
|
||||
Log(wxString::Format("Date selected: %s", date.FormatDate()));
|
||||
UpdateSelectedLabel();
|
||||
m_datePicker->SetValue(date);
|
||||
}
|
||||
|
||||
void OnDayChanged(wxCalendarEvent& event)
|
||||
{
|
||||
Log(wxString::Format("Day changed: %d", event.GetDate().GetDay()));
|
||||
}
|
||||
|
||||
void OnMonthChanged(wxCalendarEvent& event)
|
||||
{
|
||||
wxDateTime date = event.GetDate();
|
||||
Log(wxString::Format("Month changed: %s %d",
|
||||
wxDateTime::GetMonthName(date.GetMonth()), date.GetYear()));
|
||||
}
|
||||
|
||||
void OnYearChanged(wxCalendarEvent& event)
|
||||
{
|
||||
Log(wxString::Format("Year changed: %d", event.GetDate().GetYear()));
|
||||
}
|
||||
|
||||
void OnDoubleClicked(wxCalendarEvent& event)
|
||||
{
|
||||
wxDateTime date = event.GetDate();
|
||||
Log(wxString::Format("Double-clicked: %s", date.FormatDate()));
|
||||
}
|
||||
|
||||
void OnPickerChanged(wxDateEvent& event)
|
||||
{
|
||||
wxDateTime date = event.GetDate();
|
||||
m_calendar->SetDate(date);
|
||||
Log(wxString::Format("Date picker changed: %s", date.FormatDate()));
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
void OnToday(wxCommandEvent& event)
|
||||
{
|
||||
m_calendar->SetDate(wxDateTime::Today());
|
||||
m_datePicker->SetValue(wxDateTime::Today());
|
||||
Log("Navigated to today");
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
void OnPrevMonth(wxCommandEvent& event)
|
||||
{
|
||||
wxDateTime date = m_calendar->GetDate();
|
||||
date -= wxDateSpan::Month();
|
||||
m_calendar->SetDate(date);
|
||||
Log(wxString::Format("Previous month: %s", date.FormatDate()));
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
void OnNextMonth(wxCommandEvent& event)
|
||||
{
|
||||
wxDateTime date = m_calendar->GetDate();
|
||||
date += wxDateSpan::Month();
|
||||
m_calendar->SetDate(date);
|
||||
Log(wxString::Format("Next month: %s", date.FormatDate()));
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
void OnPrevYear(wxCommandEvent& event)
|
||||
{
|
||||
wxDateTime date = m_calendar->GetDate();
|
||||
date -= wxDateSpan::Year();
|
||||
m_calendar->SetDate(date);
|
||||
Log(wxString::Format("Previous year: %d", date.GetYear()));
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
void OnNextYear(wxCommandEvent& event)
|
||||
{
|
||||
wxDateTime date = m_calendar->GetDate();
|
||||
date += wxDateSpan::Year();
|
||||
m_calendar->SetDate(date);
|
||||
Log(wxString::Format("Next year: %d", date.GetYear()));
|
||||
UpdateSelectedLabel();
|
||||
}
|
||||
|
||||
wxCalendarCtrl* m_calendar;
|
||||
wxDatePickerCtrl* m_datePicker;
|
||||
wxStaticText* m_selectedLabel;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class CalendarApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
CalendarFrame* frame = new CalendarFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(CalendarApp);
|
||||
213
tests/apps/standalone/clipboard/clipboard_test.cpp
Normal file
213
tests/apps/standalone/clipboard/clipboard_test.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// wxClipboard Test - Tests clipboard functionality in WASM
|
||||
// KiCad uses clipboard for copy/paste of schematic symbols, PCB components, text
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/clipbrd.h"
|
||||
#include "wx/dataobj.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class ClipboardTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class ClipboardTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
ClipboardTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_input;
|
||||
wxTextCtrl* m_output;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnCopyText(wxCommandEvent& evt);
|
||||
void OnPasteText(wxCommandEvent& evt);
|
||||
void OnClearClipboard(wxCommandEvent& evt);
|
||||
void OnCheckClipboard(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_COPY_TEXT = wxID_HIGHEST + 1,
|
||||
ID_PASTE_TEXT,
|
||||
ID_CLEAR_CLIPBOARD,
|
||||
ID_CHECK_CLIPBOARD
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(ClipboardTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_COPY_TEXT, ClipboardTestFrame::OnCopyText)
|
||||
EVT_BUTTON(ID_PASTE_TEXT, ClipboardTestFrame::OnPasteText)
|
||||
EVT_BUTTON(ID_CLEAR_CLIPBOARD, ClipboardTestFrame::OnClearClipboard)
|
||||
EVT_BUTTON(ID_CHECK_CLIPBOARD, ClipboardTestFrame::OnCheckClipboard)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(ClipboardTestApp);
|
||||
|
||||
bool ClipboardTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
ClipboardTestFrame* frame = new ClipboardTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
ClipboardTestFrame::ClipboardTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxClipboard WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 500))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxClipboard Test\n\n"
|
||||
"Tests clipboard operations that KiCad uses for copy/paste.\n"
|
||||
"Note: Browser clipboard access may be restricted.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Input section
|
||||
wxStaticBoxSizer* inputBox = new wxStaticBoxSizer(wxVERTICAL, this, "Text to Copy");
|
||||
m_input = new wxTextCtrl(this, wxID_ANY, "Sample text for clipboard test",
|
||||
wxDefaultPosition, wxSize(-1, 60), wxTE_MULTILINE);
|
||||
inputBox->Add(m_input, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(inputBox, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
buttonSizer->Add(new wxButton(this, ID_COPY_TEXT, "Copy to Clipboard"), 0, wxALL, 5);
|
||||
buttonSizer->Add(new wxButton(this, ID_PASTE_TEXT, "Paste from Clipboard"), 0, wxALL, 5);
|
||||
buttonSizer->Add(new wxButton(this, ID_CHECK_CLIPBOARD, "Check Clipboard"), 0, wxALL, 5);
|
||||
buttonSizer->Add(new wxButton(this, ID_CLEAR_CLIPBOARD, "Clear Clipboard"), 0, wxALL, 5);
|
||||
mainSizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
// Output section
|
||||
wxStaticBoxSizer* outputBox = new wxStaticBoxSizer(wxVERTICAL, this, "Pasted Text");
|
||||
m_output = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 60), wxTE_MULTILINE | wxTE_READONLY);
|
||||
outputBox->Add(m_output, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(outputBox, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
// Log section
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - Test clipboard operations");
|
||||
|
||||
LogEvent("Clipboard test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[CLIPBOARD_TEST] wxClipboard test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClipboardTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[CLIPBOARD_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClipboardTestFrame::OnCopyText(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Attempting to copy text to clipboard...");
|
||||
|
||||
wxString text = m_input->GetValue();
|
||||
if (text.IsEmpty()) {
|
||||
LogEvent("ERROR: No text to copy");
|
||||
return;
|
||||
}
|
||||
|
||||
if (wxTheClipboard->Open()) {
|
||||
wxTheClipboard->SetData(new wxTextDataObject(text));
|
||||
wxTheClipboard->Close();
|
||||
LogEvent(wxString::Format("SUCCESS: Copied %d characters to clipboard", (int)text.Length()));
|
||||
} else {
|
||||
LogEvent("ERROR: Could not open clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
void ClipboardTestFrame::OnPasteText(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Attempting to paste from clipboard...");
|
||||
|
||||
if (wxTheClipboard->Open()) {
|
||||
if (wxTheClipboard->IsSupported(wxDF_TEXT) ||
|
||||
wxTheClipboard->IsSupported(wxDF_UNICODETEXT)) {
|
||||
wxTextDataObject data;
|
||||
wxTheClipboard->GetData(data);
|
||||
wxString text = data.GetText();
|
||||
m_output->SetValue(text);
|
||||
LogEvent(wxString::Format("SUCCESS: Pasted %d characters from clipboard", (int)text.Length()));
|
||||
} else {
|
||||
LogEvent("WARNING: No text data in clipboard");
|
||||
m_output->SetValue("");
|
||||
}
|
||||
wxTheClipboard->Close();
|
||||
} else {
|
||||
LogEvent("ERROR: Could not open clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
void ClipboardTestFrame::OnClearClipboard(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Attempting to clear clipboard...");
|
||||
|
||||
if (wxTheClipboard->Open()) {
|
||||
wxTheClipboard->Clear();
|
||||
wxTheClipboard->Close();
|
||||
LogEvent("SUCCESS: Clipboard cleared");
|
||||
} else {
|
||||
LogEvent("ERROR: Could not open clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
void ClipboardTestFrame::OnCheckClipboard(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Checking clipboard contents...");
|
||||
|
||||
if (wxTheClipboard->Open()) {
|
||||
bool hasText = wxTheClipboard->IsSupported(wxDF_TEXT) ||
|
||||
wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
|
||||
bool hasBitmap = wxTheClipboard->IsSupported(wxDF_BITMAP);
|
||||
bool hasFiles = wxTheClipboard->IsSupported(wxDF_FILENAME);
|
||||
|
||||
wxString status = "Clipboard contains: ";
|
||||
if (hasText) status += "TEXT ";
|
||||
if (hasBitmap) status += "BITMAP ";
|
||||
if (hasFiles) status += "FILES ";
|
||||
if (!hasText && !hasBitmap && !hasFiles) status += "(empty or unsupported format)";
|
||||
|
||||
LogEvent(status);
|
||||
wxTheClipboard->Close();
|
||||
} else {
|
||||
LogEvent("ERROR: Could not open clipboard");
|
||||
}
|
||||
}
|
||||
252
tests/apps/standalone/collapsible/collapsible_test.cpp
Normal file
252
tests/apps/standalone/collapsible/collapsible_test.cpp
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
// wxCollapsiblePane Test - Tests collapsible pane in WASM
|
||||
// KiCad uses collapsible panes for property panel grouping
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/collpane.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class CollapsibleTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class CollapsibleTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
CollapsibleTestFrame();
|
||||
|
||||
private:
|
||||
wxCollapsiblePane* m_pane1;
|
||||
wxCollapsiblePane* m_pane2;
|
||||
wxCollapsiblePane* m_pane3;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void CreatePaneContents(wxCollapsiblePane* pane, const wxString& type);
|
||||
|
||||
void OnPaneChanged(wxCollapsiblePaneEvent& evt);
|
||||
void OnExpandAll(wxCommandEvent& evt);
|
||||
void OnCollapseAll(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_PANE_1 = wxID_HIGHEST + 1,
|
||||
ID_PANE_2,
|
||||
ID_PANE_3,
|
||||
ID_EXPAND_ALL,
|
||||
ID_COLLAPSE_ALL
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(CollapsibleTestFrame, wxFrame)
|
||||
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_1, CollapsibleTestFrame::OnPaneChanged)
|
||||
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_2, CollapsibleTestFrame::OnPaneChanged)
|
||||
EVT_COLLAPSIBLEPANE_CHANGED(ID_PANE_3, CollapsibleTestFrame::OnPaneChanged)
|
||||
EVT_BUTTON(ID_EXPAND_ALL, CollapsibleTestFrame::OnExpandAll)
|
||||
EVT_BUTTON(ID_COLLAPSE_ALL, CollapsibleTestFrame::OnCollapseAll)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(CollapsibleTestApp);
|
||||
|
||||
bool CollapsibleTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
CollapsibleTestFrame* frame = new CollapsibleTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
CollapsibleTestFrame::CollapsibleTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxCollapsiblePane WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 700))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxCollapsiblePane Test\n\n"
|
||||
"KiCad uses collapsible panes for grouping properties in property panels.\n"
|
||||
"Click the arrows to expand/collapse each section.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Button bar
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_EXPAND_ALL, "Expand All"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_COLLAPSE_ALL, "Collapse All"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// Scrolled window to hold collapsible panes
|
||||
wxScrolledWindow* scrollWin = new wxScrolledWindow(this, wxID_ANY,
|
||||
wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
scrollWin->SetScrollRate(0, 10);
|
||||
|
||||
wxBoxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Collapsible pane 1: General Properties (like KiCad component properties)
|
||||
m_pane1 = new wxCollapsiblePane(scrollWin, ID_PANE_1, "General Properties");
|
||||
CreatePaneContents(m_pane1, "general");
|
||||
scrollSizer->Add(m_pane1, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Collapsible pane 2: Position Properties
|
||||
m_pane2 = new wxCollapsiblePane(scrollWin, ID_PANE_2, "Position & Orientation");
|
||||
CreatePaneContents(m_pane2, "position");
|
||||
scrollSizer->Add(m_pane2, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Collapsible pane 3: Display Properties
|
||||
m_pane3 = new wxCollapsiblePane(scrollWin, ID_PANE_3, "Display Options");
|
||||
CreatePaneContents(m_pane3, "display");
|
||||
scrollSizer->Add(m_pane3, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
scrollWin->SetSizer(scrollSizer);
|
||||
mainSizer->Add(scrollWin, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - wxCollapsiblePane test");
|
||||
|
||||
// Expand first pane by default
|
||||
m_pane1->Expand();
|
||||
|
||||
LogEvent("CollapsiblePane test app started");
|
||||
LogEvent("3 collapsible sections created");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[COLLAPSIBLE_TEST] wxCollapsiblePane test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CollapsibleTestFrame::CreatePaneContents(wxCollapsiblePane* pane, const wxString& type)
|
||||
{
|
||||
wxWindow* paneWin = pane->GetPane();
|
||||
wxBoxSizer* paneSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
if (type == "general") {
|
||||
// KiCad-like component properties
|
||||
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 10, 10);
|
||||
grid->AddGrowableCol(1);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Reference:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "R1"), 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Value:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "10k"), 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Footprint:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "Resistor_SMD:R_0402"), 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Datasheet:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "~"), 1, wxEXPAND);
|
||||
|
||||
paneSizer->Add(grid, 0, wxEXPAND | wxALL, 10);
|
||||
}
|
||||
else if (type == "position") {
|
||||
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 10, 10);
|
||||
grid->AddGrowableCol(1);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "X Position:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "100.5 mm"), 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Y Position:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(new wxTextCtrl(paneWin, wxID_ANY, "50.25 mm"), 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Rotation:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
wxChoice* rotChoice = new wxChoice(paneWin, wxID_ANY);
|
||||
rotChoice->Append("0°");
|
||||
rotChoice->Append("90°");
|
||||
rotChoice->Append("180°");
|
||||
rotChoice->Append("270°");
|
||||
rotChoice->SetSelection(1);
|
||||
grid->Add(rotChoice, 1, wxEXPAND);
|
||||
|
||||
grid->Add(new wxStaticText(paneWin, wxID_ANY, "Side:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
wxChoice* sideChoice = new wxChoice(paneWin, wxID_ANY);
|
||||
sideChoice->Append("Front");
|
||||
sideChoice->Append("Back");
|
||||
sideChoice->SetSelection(0);
|
||||
grid->Add(sideChoice, 1, wxEXPAND);
|
||||
|
||||
paneSizer->Add(grid, 0, wxEXPAND | wxALL, 10);
|
||||
}
|
||||
else if (type == "display") {
|
||||
wxBoxSizer* checkSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Reference"), 0, wxALL, 5);
|
||||
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Value"), 0, wxALL, 5);
|
||||
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Show Footprint"), 0, wxALL, 5);
|
||||
checkSizer->Add(new wxCheckBox(paneWin, wxID_ANY, "Highlight on Selection"), 0, wxALL, 5);
|
||||
|
||||
paneSizer->Add(checkSizer, 0, wxEXPAND | wxALL, 5);
|
||||
}
|
||||
|
||||
paneWin->SetSizer(paneSizer);
|
||||
}
|
||||
|
||||
void CollapsibleTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[COLLAPSIBLE_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void CollapsibleTestFrame::OnPaneChanged(wxCollapsiblePaneEvent& evt)
|
||||
{
|
||||
wxString paneName;
|
||||
switch (evt.GetId()) {
|
||||
case ID_PANE_1: paneName = "General Properties"; break;
|
||||
case ID_PANE_2: paneName = "Position & Orientation"; break;
|
||||
case ID_PANE_3: paneName = "Display Options"; break;
|
||||
default: paneName = "Unknown"; break;
|
||||
}
|
||||
|
||||
LogEvent(wxString::Format("Pane '%s' %s",
|
||||
paneName,
|
||||
evt.GetCollapsed() ? "collapsed" : "expanded"));
|
||||
|
||||
// Relayout the parent
|
||||
Layout();
|
||||
}
|
||||
|
||||
void CollapsibleTestFrame::OnExpandAll(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_pane1->Expand();
|
||||
m_pane2->Expand();
|
||||
m_pane3->Expand();
|
||||
Layout();
|
||||
LogEvent("All panes expanded");
|
||||
}
|
||||
|
||||
void CollapsibleTestFrame::OnCollapseAll(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_pane1->Collapse();
|
||||
m_pane2->Collapse();
|
||||
m_pane3->Collapse();
|
||||
Layout();
|
||||
LogEvent("All panes collapsed");
|
||||
}
|
||||
459
tests/apps/standalone/dataview/dataview_test.cpp
Normal file
459
tests/apps/standalone/dataview/dataview_test.cpp
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
// wxDataViewCtrl Test - Tests DataViewCtrl in WASM
|
||||
// KiCad uses DataViewCtrl for Zone Manager, Net Inspector, Library browsers
|
||||
// This is CRITICAL for KiCad functionality
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/dataview.h"
|
||||
#include "wx/notebook.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class DataViewTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class DataViewTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
DataViewTestFrame();
|
||||
|
||||
private:
|
||||
wxDataViewListCtrl* m_listCtrl;
|
||||
wxDataViewTreeCtrl* m_treeCtrl;
|
||||
wxTextCtrl* m_log;
|
||||
wxNotebook* m_notebook;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void PopulateList();
|
||||
void PopulateTree();
|
||||
|
||||
// List events
|
||||
void OnListSelectionChanged(wxDataViewEvent& evt);
|
||||
void OnListItemActivated(wxDataViewEvent& evt);
|
||||
void OnListColumnHeaderClick(wxDataViewEvent& evt);
|
||||
void OnListItemStartEditing(wxDataViewEvent& evt);
|
||||
void OnListItemEditingDone(wxDataViewEvent& evt);
|
||||
|
||||
// Tree events
|
||||
void OnTreeSelectionChanged(wxDataViewEvent& evt);
|
||||
void OnTreeItemExpanding(wxDataViewEvent& evt);
|
||||
void OnTreeItemCollapsing(wxDataViewEvent& evt);
|
||||
void OnTreeItemActivated(wxDataViewEvent& evt);
|
||||
|
||||
// Button handlers
|
||||
void OnAddListItem(wxCommandEvent& evt);
|
||||
void OnRemoveListItem(wxCommandEvent& evt);
|
||||
void OnClearList(wxCommandEvent& evt);
|
||||
void OnExpandTree(wxCommandEvent& evt);
|
||||
void OnCollapseTree(wxCommandEvent& evt);
|
||||
void OnAddTreeItem(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_LIST = wxID_HIGHEST + 1,
|
||||
ID_TREE,
|
||||
ID_ADD_LIST_ITEM,
|
||||
ID_REMOVE_LIST_ITEM,
|
||||
ID_CLEAR_LIST,
|
||||
ID_EXPAND_TREE,
|
||||
ID_COLLAPSE_TREE,
|
||||
ID_ADD_TREE_ITEM
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(DataViewTestFrame, wxFrame)
|
||||
// List events
|
||||
EVT_DATAVIEW_SELECTION_CHANGED(ID_LIST, DataViewTestFrame::OnListSelectionChanged)
|
||||
EVT_DATAVIEW_ITEM_ACTIVATED(ID_LIST, DataViewTestFrame::OnListItemActivated)
|
||||
EVT_DATAVIEW_COLUMN_HEADER_CLICK(ID_LIST, DataViewTestFrame::OnListColumnHeaderClick)
|
||||
EVT_DATAVIEW_ITEM_START_EDITING(ID_LIST, DataViewTestFrame::OnListItemStartEditing)
|
||||
EVT_DATAVIEW_ITEM_EDITING_DONE(ID_LIST, DataViewTestFrame::OnListItemEditingDone)
|
||||
// Tree events
|
||||
EVT_DATAVIEW_SELECTION_CHANGED(ID_TREE, DataViewTestFrame::OnTreeSelectionChanged)
|
||||
EVT_DATAVIEW_ITEM_EXPANDING(ID_TREE, DataViewTestFrame::OnTreeItemExpanding)
|
||||
EVT_DATAVIEW_ITEM_COLLAPSING(ID_TREE, DataViewTestFrame::OnTreeItemCollapsing)
|
||||
EVT_DATAVIEW_ITEM_ACTIVATED(ID_TREE, DataViewTestFrame::OnTreeItemActivated)
|
||||
// Buttons
|
||||
EVT_BUTTON(ID_ADD_LIST_ITEM, DataViewTestFrame::OnAddListItem)
|
||||
EVT_BUTTON(ID_REMOVE_LIST_ITEM, DataViewTestFrame::OnRemoveListItem)
|
||||
EVT_BUTTON(ID_CLEAR_LIST, DataViewTestFrame::OnClearList)
|
||||
EVT_BUTTON(ID_EXPAND_TREE, DataViewTestFrame::OnExpandTree)
|
||||
EVT_BUTTON(ID_COLLAPSE_TREE, DataViewTestFrame::OnCollapseTree)
|
||||
EVT_BUTTON(ID_ADD_TREE_ITEM, DataViewTestFrame::OnAddTreeItem)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(DataViewTestApp);
|
||||
|
||||
bool DataViewTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
DataViewTestFrame* frame = new DataViewTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
DataViewTestFrame::DataViewTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxDataViewCtrl WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 700))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxDataViewCtrl Test\n\n"
|
||||
"KiCad uses DataViewCtrl for Zone Manager, Net Inspector, and Library browsers.\n"
|
||||
"Test both list and tree views.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Notebook for list and tree tabs
|
||||
m_notebook = new wxNotebook(this, wxID_ANY);
|
||||
|
||||
// === List Tab ===
|
||||
wxPanel* listPanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* listSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// List button bar
|
||||
wxBoxSizer* listBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
listBtnSizer->Add(new wxButton(listPanel, ID_ADD_LIST_ITEM, "Add Item"), 0, wxALL, 5);
|
||||
listBtnSizer->Add(new wxButton(listPanel, ID_REMOVE_LIST_ITEM, "Remove Selected"), 0, wxALL, 5);
|
||||
listBtnSizer->Add(new wxButton(listPanel, ID_CLEAR_LIST, "Clear All"), 0, wxALL, 5);
|
||||
listSizer->Add(listBtnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// DataViewListCtrl - like KiCad Zone Manager
|
||||
m_listCtrl = new wxDataViewListCtrl(listPanel, ID_LIST, wxDefaultPosition, wxSize(-1, 200));
|
||||
|
||||
// Add columns similar to KiCad Zone Manager
|
||||
m_listCtrl->AppendTextColumn("Zone Name", wxDATAVIEW_CELL_EDITABLE, 150);
|
||||
m_listCtrl->AppendTextColumn("Net", wxDATAVIEW_CELL_INERT, 100);
|
||||
m_listCtrl->AppendTextColumn("Layer", wxDATAVIEW_CELL_INERT, 80);
|
||||
m_listCtrl->AppendTextColumn("Priority", wxDATAVIEW_CELL_EDITABLE, 60);
|
||||
m_listCtrl->AppendTextColumn("Fill Mode", wxDATAVIEW_CELL_INERT, 80);
|
||||
|
||||
listSizer->Add(m_listCtrl, 1, wxEXPAND | wxALL, 10);
|
||||
listPanel->SetSizer(listSizer);
|
||||
m_notebook->AddPage(listPanel, "List View");
|
||||
|
||||
// === Tree Tab ===
|
||||
wxPanel* treePanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* treeSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Tree button bar
|
||||
wxBoxSizer* treeBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
treeBtnSizer->Add(new wxButton(treePanel, ID_EXPAND_TREE, "Expand All"), 0, wxALL, 5);
|
||||
treeBtnSizer->Add(new wxButton(treePanel, ID_COLLAPSE_TREE, "Collapse All"), 0, wxALL, 5);
|
||||
treeBtnSizer->Add(new wxButton(treePanel, ID_ADD_TREE_ITEM, "Add Item"), 0, wxALL, 5);
|
||||
treeSizer->Add(treeBtnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// DataViewTreeCtrl - like KiCad Library Browser
|
||||
m_treeCtrl = new wxDataViewTreeCtrl(treePanel, ID_TREE, wxDefaultPosition, wxSize(-1, 200));
|
||||
|
||||
treeSizer->Add(m_treeCtrl, 1, wxEXPAND | wxALL, 10);
|
||||
treePanel->SetSizer(treeSizer);
|
||||
m_notebook->AddPage(treePanel, "Tree View");
|
||||
|
||||
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
// Populate controls
|
||||
PopulateList();
|
||||
PopulateTree();
|
||||
|
||||
LogEvent("DataViewCtrl test app started");
|
||||
LogEvent("List populated with KiCad Zone Manager-like data");
|
||||
LogEvent("Tree populated with KiCad Library-like hierarchy");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DATAVIEW_TEST] wxDataViewCtrl test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void DataViewTestFrame::PopulateList()
|
||||
{
|
||||
// Add Zone Manager-like data
|
||||
wxVector<wxVariant> data;
|
||||
|
||||
data.clear();
|
||||
data.push_back(wxVariant("Zone_GND_Top"));
|
||||
data.push_back(wxVariant("GND"));
|
||||
data.push_back(wxVariant("F.Cu"));
|
||||
data.push_back(wxVariant("0"));
|
||||
data.push_back(wxVariant("Solid"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
|
||||
data.clear();
|
||||
data.push_back(wxVariant("Zone_GND_Bottom"));
|
||||
data.push_back(wxVariant("GND"));
|
||||
data.push_back(wxVariant("B.Cu"));
|
||||
data.push_back(wxVariant("0"));
|
||||
data.push_back(wxVariant("Solid"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
|
||||
data.clear();
|
||||
data.push_back(wxVariant("Zone_VCC"));
|
||||
data.push_back(wxVariant("VCC"));
|
||||
data.push_back(wxVariant("F.Cu"));
|
||||
data.push_back(wxVariant("1"));
|
||||
data.push_back(wxVariant("Hatched"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
|
||||
data.clear();
|
||||
data.push_back(wxVariant("Zone_3V3"));
|
||||
data.push_back(wxVariant("3V3"));
|
||||
data.push_back(wxVariant("B.Cu"));
|
||||
data.push_back(wxVariant("2"));
|
||||
data.push_back(wxVariant("Hatched"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
|
||||
data.clear();
|
||||
data.push_back(wxVariant("Zone_Shield"));
|
||||
data.push_back(wxVariant("GND"));
|
||||
data.push_back(wxVariant("Edge.Cuts"));
|
||||
data.push_back(wxVariant("3"));
|
||||
data.push_back(wxVariant("Solid"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
|
||||
// Add more items for virtual scrolling test
|
||||
for (int i = 1; i <= 20; i++) {
|
||||
data.clear();
|
||||
data.push_back(wxVariant(wxString::Format("Zone_Custom_%d", i)));
|
||||
data.push_back(wxVariant(wxString::Format("Net_%d", i)));
|
||||
data.push_back(wxVariant("In1.Cu"));
|
||||
data.push_back(wxVariant(wxString::Format("%d", i + 3)));
|
||||
data.push_back(wxVariant("Solid"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::PopulateTree()
|
||||
{
|
||||
// Create Library Browser-like hierarchy
|
||||
wxDataViewItem root = m_treeCtrl->AppendContainer(wxDataViewItem(), "Libraries");
|
||||
|
||||
// Symbol Libraries
|
||||
wxDataViewItem symbols = m_treeCtrl->AppendContainer(root, "Symbol Libraries");
|
||||
wxDataViewItem device = m_treeCtrl->AppendContainer(symbols, "Device");
|
||||
m_treeCtrl->AppendItem(device, "R - Resistor");
|
||||
m_treeCtrl->AppendItem(device, "C - Capacitor");
|
||||
m_treeCtrl->AppendItem(device, "L - Inductor");
|
||||
m_treeCtrl->AppendItem(device, "D - Diode");
|
||||
m_treeCtrl->AppendItem(device, "LED");
|
||||
|
||||
wxDataViewItem connector = m_treeCtrl->AppendContainer(symbols, "Connector");
|
||||
m_treeCtrl->AppendItem(connector, "Conn_01x02");
|
||||
m_treeCtrl->AppendItem(connector, "Conn_01x04");
|
||||
m_treeCtrl->AppendItem(connector, "USB_B");
|
||||
m_treeCtrl->AppendItem(connector, "USB_C");
|
||||
|
||||
wxDataViewItem mcu = m_treeCtrl->AppendContainer(symbols, "MCU_ST");
|
||||
m_treeCtrl->AppendItem(mcu, "STM32F103C8");
|
||||
m_treeCtrl->AppendItem(mcu, "STM32F401RE");
|
||||
m_treeCtrl->AppendItem(mcu, "STM32G431KB");
|
||||
|
||||
// Footprint Libraries
|
||||
wxDataViewItem footprints = m_treeCtrl->AppendContainer(root, "Footprint Libraries");
|
||||
wxDataViewItem resistors = m_treeCtrl->AppendContainer(footprints, "Resistor_SMD");
|
||||
m_treeCtrl->AppendItem(resistors, "R_0402");
|
||||
m_treeCtrl->AppendItem(resistors, "R_0603");
|
||||
m_treeCtrl->AppendItem(resistors, "R_0805");
|
||||
m_treeCtrl->AppendItem(resistors, "R_1206");
|
||||
|
||||
wxDataViewItem capacitors = m_treeCtrl->AppendContainer(footprints, "Capacitor_SMD");
|
||||
m_treeCtrl->AppendItem(capacitors, "C_0402");
|
||||
m_treeCtrl->AppendItem(capacitors, "C_0603");
|
||||
m_treeCtrl->AppendItem(capacitors, "C_0805");
|
||||
|
||||
// Expand root
|
||||
m_treeCtrl->Expand(root);
|
||||
}
|
||||
|
||||
void DataViewTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DATAVIEW_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
// List event handlers
|
||||
void DataViewTestFrame::OnListSelectionChanged(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
int row = m_listCtrl->ItemToRow(item);
|
||||
wxVariant val;
|
||||
m_listCtrl->GetValue(val, row, 0);
|
||||
LogEvent(wxString::Format("List: Selection changed to row %d: '%s'", row, val.GetString()));
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnListItemActivated(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
int row = m_listCtrl->ItemToRow(item);
|
||||
wxVariant val;
|
||||
m_listCtrl->GetValue(val, row, 0);
|
||||
LogEvent(wxString::Format("List: Item activated (double-click) row %d: '%s'", row, val.GetString()));
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnListColumnHeaderClick(wxDataViewEvent& evt)
|
||||
{
|
||||
int col = evt.GetColumn();
|
||||
wxString colName = m_listCtrl->GetColumn(col)->GetTitle();
|
||||
LogEvent(wxString::Format("List: Column header clicked: '%s' (col %d)", colName, col));
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnListItemStartEditing(wxDataViewEvent& evt)
|
||||
{
|
||||
int row = m_listCtrl->ItemToRow(evt.GetItem());
|
||||
int col = evt.GetColumn();
|
||||
LogEvent(wxString::Format("List: Start editing row %d, col %d", row, col));
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnListItemEditingDone(wxDataViewEvent& evt)
|
||||
{
|
||||
int row = m_listCtrl->ItemToRow(evt.GetItem());
|
||||
int col = evt.GetColumn();
|
||||
wxString newVal = evt.GetValue().GetString();
|
||||
LogEvent(wxString::Format("List: Editing done row %d, col %d, new value: '%s'", row, col, newVal));
|
||||
}
|
||||
|
||||
// Tree event handlers
|
||||
void DataViewTestFrame::OnTreeSelectionChanged(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
wxString text = m_treeCtrl->GetItemText(item);
|
||||
LogEvent(wxString::Format("Tree: Selection changed to '%s'", text));
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnTreeItemExpanding(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
wxString text = m_treeCtrl->GetItemText(item);
|
||||
LogEvent(wxString::Format("Tree: Expanding '%s'", text));
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnTreeItemCollapsing(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
wxString text = m_treeCtrl->GetItemText(item);
|
||||
LogEvent(wxString::Format("Tree: Collapsing '%s'", text));
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnTreeItemActivated(wxDataViewEvent& evt)
|
||||
{
|
||||
wxDataViewItem item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
wxString text = m_treeCtrl->GetItemText(item);
|
||||
LogEvent(wxString::Format("Tree: Item activated (double-click) '%s'", text));
|
||||
}
|
||||
}
|
||||
|
||||
// Button handlers
|
||||
void DataViewTestFrame::OnAddListItem(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
static int itemNum = 1;
|
||||
wxVector<wxVariant> data;
|
||||
data.push_back(wxVariant(wxString::Format("New_Zone_%d", itemNum)));
|
||||
data.push_back(wxVariant("NewNet"));
|
||||
data.push_back(wxVariant("F.Cu"));
|
||||
data.push_back(wxVariant(wxString::Format("%d", itemNum)));
|
||||
data.push_back(wxVariant("Solid"));
|
||||
m_listCtrl->AppendItem(data);
|
||||
LogEvent(wxString::Format("List: Added new item 'New_Zone_%d'", itemNum));
|
||||
itemNum++;
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnRemoveListItem(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
int row = m_listCtrl->GetSelectedRow();
|
||||
if (row != wxNOT_FOUND) {
|
||||
wxVariant val;
|
||||
m_listCtrl->GetValue(val, row, 0);
|
||||
m_listCtrl->DeleteItem(row);
|
||||
LogEvent(wxString::Format("List: Removed item '%s' at row %d", val.GetString(), row));
|
||||
} else {
|
||||
LogEvent("List: No item selected to remove");
|
||||
}
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnClearList(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_listCtrl->DeleteAllItems();
|
||||
LogEvent("List: All items cleared");
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnExpandTree(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
// Expand all items by iterating
|
||||
wxDataViewItemArray children;
|
||||
m_treeCtrl->GetStore()->GetChildren(wxDataViewItem(), children);
|
||||
for (size_t i = 0; i < children.GetCount(); i++) {
|
||||
m_treeCtrl->Expand(children[i]);
|
||||
wxDataViewItemArray subChildren;
|
||||
m_treeCtrl->GetStore()->GetChildren(children[i], subChildren);
|
||||
for (size_t j = 0; j < subChildren.GetCount(); j++) {
|
||||
m_treeCtrl->Expand(subChildren[j]);
|
||||
}
|
||||
}
|
||||
LogEvent("Tree: All items expanded");
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnCollapseTree(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxDataViewItemArray children;
|
||||
m_treeCtrl->GetStore()->GetChildren(wxDataViewItem(), children);
|
||||
for (size_t i = 0; i < children.GetCount(); i++) {
|
||||
m_treeCtrl->Collapse(children[i]);
|
||||
}
|
||||
LogEvent("Tree: All items collapsed");
|
||||
}
|
||||
|
||||
void DataViewTestFrame::OnAddTreeItem(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxDataViewItem sel = m_treeCtrl->GetSelection();
|
||||
if (sel.IsOk()) {
|
||||
static int itemNum = 1;
|
||||
m_treeCtrl->AppendItem(sel, wxString::Format("New Item %d", itemNum++));
|
||||
m_treeCtrl->Expand(sel);
|
||||
LogEvent(wxString::Format("Tree: Added new item under '%s'", m_treeCtrl->GetItemText(sel)));
|
||||
} else {
|
||||
LogEvent("Tree: No item selected - select a parent first");
|
||||
}
|
||||
}
|
||||
306
tests/apps/standalone/dataviewvirtual/dataviewvirtual_test.cpp
Normal file
306
tests/apps/standalone/dataviewvirtual/dataviewvirtual_test.cpp
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// wxDataViewCtrl Virtual Mode Test - Zone Manager/Net Inspector simulation
|
||||
// Tests wxDataViewCtrl with virtual data model for large datasets
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/dataview.h"
|
||||
#include "wx/splitter.h"
|
||||
|
||||
// Virtual data model for large datasets (like Zone Manager or Net Inspector)
|
||||
class VirtualNetModel : public wxDataViewVirtualListModel
|
||||
{
|
||||
public:
|
||||
VirtualNetModel(int itemCount = 10000) : m_itemCount(itemCount)
|
||||
{
|
||||
// Pre-generate some sample data patterns
|
||||
m_netClasses = {"Default", "Power", "Signal", "Clock", "Differential"};
|
||||
}
|
||||
|
||||
virtual unsigned int GetColumnCount() const override { return 4; }
|
||||
|
||||
virtual wxString GetColumnType(unsigned int col) const override
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
virtual unsigned int GetCount() const override { return m_itemCount; }
|
||||
|
||||
virtual void GetValueByRow(wxVariant& variant, unsigned int row, unsigned int col) const override
|
||||
{
|
||||
switch (col)
|
||||
{
|
||||
case 0: // Net Name
|
||||
variant = wxString::Format("NET_%05d", row);
|
||||
break;
|
||||
case 1: // Net Class
|
||||
variant = m_netClasses[row % m_netClasses.size()];
|
||||
break;
|
||||
case 2: // Connection Count
|
||||
variant = wxString::Format("%d", (row * 7 + 3) % 50);
|
||||
break;
|
||||
case 3: // Length (mm)
|
||||
variant = wxString::Format("%.2f", (row * 13 + 5) % 1000 / 10.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool SetValueByRow(const wxVariant& variant, unsigned int row, unsigned int col) override
|
||||
{
|
||||
return false; // Read-only for this test
|
||||
}
|
||||
|
||||
void SetItemCount(int count)
|
||||
{
|
||||
m_itemCount = count;
|
||||
Reset(count);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_itemCount;
|
||||
std::vector<wxString> m_netClasses;
|
||||
};
|
||||
|
||||
// Zone model for Zone Manager simulation
|
||||
class VirtualZoneModel : public wxDataViewVirtualListModel
|
||||
{
|
||||
public:
|
||||
VirtualZoneModel(int itemCount = 1000) : m_itemCount(itemCount)
|
||||
{
|
||||
m_layers = {"F.Cu", "B.Cu", "In1.Cu", "In2.Cu"};
|
||||
m_priorities = {"0", "1", "2", "3"};
|
||||
}
|
||||
|
||||
virtual unsigned int GetColumnCount() const override { return 5; }
|
||||
|
||||
virtual wxString GetColumnType(unsigned int col) const override
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
virtual unsigned int GetCount() const override { return m_itemCount; }
|
||||
|
||||
virtual void GetValueByRow(wxVariant& variant, unsigned int row, unsigned int col) const override
|
||||
{
|
||||
switch (col)
|
||||
{
|
||||
case 0: // Zone Name
|
||||
variant = wxString::Format("Zone_%03d", row);
|
||||
break;
|
||||
case 1: // Net
|
||||
variant = wxString::Format("NET_%04d", row % 500);
|
||||
break;
|
||||
case 2: // Layer
|
||||
variant = m_layers[row % m_layers.size()];
|
||||
break;
|
||||
case 3: // Priority
|
||||
variant = m_priorities[row % m_priorities.size()];
|
||||
break;
|
||||
case 4: // Area (mm²)
|
||||
variant = wxString::Format("%.1f", (row * 17 + 100) % 5000 / 10.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool SetValueByRow(const wxVariant& variant, unsigned int row, unsigned int col) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetItemCount(int count)
|
||||
{
|
||||
m_itemCount = count;
|
||||
Reset(count);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_itemCount;
|
||||
std::vector<wxString> m_layers;
|
||||
std::vector<wxString> m_priorities;
|
||||
};
|
||||
|
||||
class DataViewVirtualFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
DataViewVirtualFrame() : wxFrame(nullptr, wxID_ANY, "wxDataViewCtrl Virtual Mode Test",
|
||||
wxDefaultPosition, wxSize(1200, 700))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxDataViewCtrl with virtual models for Zone Manager and Net Inspector.\n"
|
||||
"Virtual mode handles 10,000+ items efficiently by only creating visible rows.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Controls
|
||||
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Item Count:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
|
||||
|
||||
wxButton* btn100 = new wxButton(mainPanel, wxID_ANY, "100");
|
||||
wxButton* btn1000 = new wxButton(mainPanel, wxID_ANY, "1,000");
|
||||
wxButton* btn10000 = new wxButton(mainPanel, wxID_ANY, "10,000");
|
||||
wxButton* btn50000 = new wxButton(mainPanel, wxID_ANY, "50,000");
|
||||
|
||||
btn100->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(100); });
|
||||
btn1000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(1000); });
|
||||
btn10000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(10000); });
|
||||
btn50000->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { SetNetCount(50000); });
|
||||
|
||||
controlSizer->Add(btn100, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btn1000, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btn10000, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btn50000, 0, wxRIGHT, 10);
|
||||
|
||||
controlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Scroll:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
|
||||
|
||||
wxButton* btnTop = new wxButton(mainPanel, wxID_ANY, "Top");
|
||||
wxButton* btnMiddle = new wxButton(mainPanel, wxID_ANY, "Middle");
|
||||
wxButton* btnBottom = new wxButton(mainPanel, wxID_ANY, "Bottom");
|
||||
|
||||
btnTop->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(0); });
|
||||
btnMiddle->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(m_netModel->GetCount() / 2); });
|
||||
btnBottom->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { ScrollTo(m_netModel->GetCount() - 1); });
|
||||
|
||||
controlSizer->Add(btnTop, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btnMiddle, 0, wxRIGHT, 2);
|
||||
controlSizer->Add(btnBottom, 0);
|
||||
|
||||
mainSizer->Add(controlSizer, 0, wxALL, 5);
|
||||
|
||||
// Count label
|
||||
m_countLabel = new wxStaticText(mainPanel, wxID_ANY, "Current items: 10,000");
|
||||
mainSizer->Add(m_countLabel, 0, wxLEFT | wxBOTTOM, 5);
|
||||
|
||||
// Splitter for two DataViewCtrls
|
||||
wxSplitterWindow* splitter = new wxSplitterWindow(mainPanel, wxID_ANY);
|
||||
|
||||
// Net Inspector panel (left)
|
||||
wxPanel* netPanel = new wxPanel(splitter);
|
||||
wxBoxSizer* netSizer = new wxBoxSizer(wxVERTICAL);
|
||||
netSizer->Add(new wxStaticText(netPanel, wxID_ANY, "Net Inspector (Virtual List - 10,000 items)"), 0, wxBOTTOM, 5);
|
||||
|
||||
m_netView = new wxDataViewCtrl(netPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
||||
wxDV_ROW_LINES | wxDV_VERT_RULES);
|
||||
|
||||
m_netModel = new VirtualNetModel(10000);
|
||||
m_netView->AssociateModel(m_netModel);
|
||||
m_netModel->DecRef(); // Model is now owned by the view
|
||||
|
||||
m_netView->AppendTextColumn("Net Name", 0, wxDATAVIEW_CELL_INERT, 120);
|
||||
m_netView->AppendTextColumn("Net Class", 1, wxDATAVIEW_CELL_INERT, 100);
|
||||
m_netView->AppendTextColumn("Connections", 2, wxDATAVIEW_CELL_INERT, 100);
|
||||
m_netView->AppendTextColumn("Length (mm)", 3, wxDATAVIEW_CELL_INERT, 100);
|
||||
|
||||
netSizer->Add(m_netView, 1, wxEXPAND);
|
||||
netPanel->SetSizer(netSizer);
|
||||
|
||||
// Zone Manager panel (right)
|
||||
wxPanel* zonePanel = new wxPanel(splitter);
|
||||
wxBoxSizer* zoneSizer = new wxBoxSizer(wxVERTICAL);
|
||||
zoneSizer->Add(new wxStaticText(zonePanel, wxID_ANY, "Zone Manager (Virtual List - 1,000 items)"), 0, wxBOTTOM, 5);
|
||||
|
||||
m_zoneView = new wxDataViewCtrl(zonePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
||||
wxDV_ROW_LINES | wxDV_VERT_RULES);
|
||||
|
||||
m_zoneModel = new VirtualZoneModel(1000);
|
||||
m_zoneView->AssociateModel(m_zoneModel);
|
||||
m_zoneModel->DecRef();
|
||||
|
||||
m_zoneView->AppendTextColumn("Zone", 0, wxDATAVIEW_CELL_INERT, 80);
|
||||
m_zoneView->AppendTextColumn("Net", 1, wxDATAVIEW_CELL_INERT, 100);
|
||||
m_zoneView->AppendTextColumn("Layer", 2, wxDATAVIEW_CELL_INERT, 80);
|
||||
m_zoneView->AppendTextColumn("Priority", 3, wxDATAVIEW_CELL_INERT, 70);
|
||||
m_zoneView->AppendTextColumn("Area (mm²)", 4, wxDATAVIEW_CELL_INERT, 90);
|
||||
|
||||
zoneSizer->Add(m_zoneView, 1, wxEXPAND);
|
||||
zonePanel->SetSizer(zoneSizer);
|
||||
|
||||
splitter->SplitVertically(netPanel, zonePanel, 500);
|
||||
mainSizer->Add(splitter, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Bind selection events
|
||||
m_netView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &DataViewVirtualFrame::OnNetSelected, this);
|
||||
m_zoneView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &DataViewVirtualFrame::OnZoneSelected, this);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("DataViewCtrl virtual mode test app started");
|
||||
|
||||
Log("DataViewVirtual test app started");
|
||||
Log("Net Inspector: 10,000 virtual items");
|
||||
Log("Zone Manager: 1,000 virtual items");
|
||||
}
|
||||
|
||||
private:
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void SetNetCount(int count)
|
||||
{
|
||||
m_netModel->SetItemCount(count);
|
||||
m_countLabel->SetLabel(wxString::Format("Current items: %d", count));
|
||||
Log(wxString::Format("Net Inspector set to %d items", count));
|
||||
}
|
||||
|
||||
void ScrollTo(int row)
|
||||
{
|
||||
wxDataViewItem item = m_netModel->GetItem(row);
|
||||
m_netView->EnsureVisible(item);
|
||||
Log(wxString::Format("Scrolled to row %d", row));
|
||||
}
|
||||
|
||||
void OnNetSelected(wxDataViewEvent& event)
|
||||
{
|
||||
wxDataViewItem item = event.GetItem();
|
||||
if (item.IsOk())
|
||||
{
|
||||
int row = m_netModel->GetRow(item);
|
||||
wxVariant val;
|
||||
m_netModel->GetValueByRow(val, row, 0);
|
||||
Log(wxString::Format("Net selected: %s (row %d)", val.GetString(), row));
|
||||
}
|
||||
}
|
||||
|
||||
void OnZoneSelected(wxDataViewEvent& event)
|
||||
{
|
||||
wxDataViewItem item = event.GetItem();
|
||||
if (item.IsOk())
|
||||
{
|
||||
int row = m_zoneModel->GetRow(item);
|
||||
wxVariant val;
|
||||
m_zoneModel->GetValueByRow(val, row, 0);
|
||||
Log(wxString::Format("Zone selected: %s (row %d)", val.GetString(), row));
|
||||
}
|
||||
}
|
||||
|
||||
wxDataViewCtrl* m_netView;
|
||||
wxDataViewCtrl* m_zoneView;
|
||||
VirtualNetModel* m_netModel;
|
||||
VirtualZoneModel* m_zoneModel;
|
||||
wxStaticText* m_countLabel;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class DataViewVirtualApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
DataViewVirtualFrame* frame = new DataViewVirtualFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(DataViewVirtualApp);
|
||||
225
tests/apps/standalone/dialog/dialog_test.cpp
Normal file
225
tests/apps/standalone/dialog/dialog_test.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// wxDialog/wxMessageBox Test - Tests modal dialogs in WASM
|
||||
// KiCad uses dialogs for alerts, confirmations, and custom dialogs
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class DialogTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class DialogTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
DialogTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnInfoDialog(wxCommandEvent& evt);
|
||||
void OnYesNoDialog(wxCommandEvent& evt);
|
||||
void OnErrorDialog(wxCommandEvent& evt);
|
||||
void OnCustomDialog(wxCommandEvent& evt);
|
||||
void OnInputDialog(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// Custom dialog class (like KiCad's property dialogs)
|
||||
class CustomTestDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
CustomTestDialog(wxWindow* parent);
|
||||
|
||||
wxString GetValue() const { return m_textCtrl->GetValue(); }
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_textCtrl;
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_INFO_DIALOG = wxID_HIGHEST + 1,
|
||||
ID_YESNO_DIALOG,
|
||||
ID_ERROR_DIALOG,
|
||||
ID_CUSTOM_DIALOG,
|
||||
ID_INPUT_DIALOG
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(DialogTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_INFO_DIALOG, DialogTestFrame::OnInfoDialog)
|
||||
EVT_BUTTON(ID_YESNO_DIALOG, DialogTestFrame::OnYesNoDialog)
|
||||
EVT_BUTTON(ID_ERROR_DIALOG, DialogTestFrame::OnErrorDialog)
|
||||
EVT_BUTTON(ID_CUSTOM_DIALOG, DialogTestFrame::OnCustomDialog)
|
||||
EVT_BUTTON(ID_INPUT_DIALOG, DialogTestFrame::OnInputDialog)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(DialogTestApp);
|
||||
|
||||
bool DialogTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
DialogTestFrame* frame = new DialogTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
DialogTestFrame::DialogTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxDialog/wxMessageBox WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 500))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxDialog and wxMessageBox Test\n\n"
|
||||
"KiCad uses dialogs for alerts, confirmations, and custom property dialogs.\n"
|
||||
"Click buttons to test different dialog types.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// wxMessageBox section
|
||||
wxStaticBoxSizer* msgBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, "wxMessageBox");
|
||||
wxBoxSizer* msgBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
msgBtnSizer->Add(new wxButton(this, ID_INFO_DIALOG, "Info Dialog"), 0, wxALL, 5);
|
||||
msgBtnSizer->Add(new wxButton(this, ID_YESNO_DIALOG, "Yes/No Dialog"), 0, wxALL, 5);
|
||||
msgBtnSizer->Add(new wxButton(this, ID_ERROR_DIALOG, "Error Dialog"), 0, wxALL, 5);
|
||||
msgBoxSizer->Add(msgBtnSizer, 0, wxALIGN_CENTER);
|
||||
mainSizer->Add(msgBoxSizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// wxDialog section
|
||||
wxStaticBoxSizer* dlgSizer = new wxStaticBoxSizer(wxVERTICAL, this, "wxDialog");
|
||||
wxBoxSizer* dlgBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
dlgBtnSizer->Add(new wxButton(this, ID_CUSTOM_DIALOG, "Custom Dialog"), 0, wxALL, 5);
|
||||
dlgBtnSizer->Add(new wxButton(this, ID_INPUT_DIALOG, "Input Dialog"), 0, wxALL, 5);
|
||||
dlgSizer->Add(dlgBtnSizer, 0, wxALIGN_CENTER);
|
||||
mainSizer->Add(dlgSizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
LogEvent("Dialog test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DIALOG_TEST] wxDialog test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void DialogTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DIALOG_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void DialogTestFrame::OnInfoDialog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Info dialog...");
|
||||
|
||||
int result = wxMessageBox("This is an informational message.\n\nKiCad uses these for status updates.",
|
||||
"Information", wxOK | wxICON_INFORMATION, this);
|
||||
|
||||
LogEvent(wxString::Format("Info dialog closed with result: %d", result));
|
||||
}
|
||||
|
||||
void DialogTestFrame::OnYesNoDialog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Yes/No dialog...");
|
||||
|
||||
int result = wxMessageBox("Do you want to save changes?\n\nKiCad uses these for confirmations.",
|
||||
"Confirm", wxYES_NO | wxCANCEL | wxICON_QUESTION, this);
|
||||
|
||||
wxString resultStr;
|
||||
switch (result) {
|
||||
case wxYES: resultStr = "YES"; break;
|
||||
case wxNO: resultStr = "NO"; break;
|
||||
case wxCANCEL: resultStr = "CANCEL"; break;
|
||||
default: resultStr = wxString::Format("Unknown (%d)", result);
|
||||
}
|
||||
LogEvent(wxString::Format("Yes/No dialog closed with: %s", resultStr));
|
||||
}
|
||||
|
||||
void DialogTestFrame::OnErrorDialog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Error dialog...");
|
||||
|
||||
int result = wxMessageBox("An error has occurred!\n\nKiCad uses these for error messages.",
|
||||
"Error", wxOK | wxICON_ERROR, this);
|
||||
|
||||
LogEvent(wxString::Format("Error dialog closed with result: %d", result));
|
||||
}
|
||||
|
||||
void DialogTestFrame::OnCustomDialog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Custom dialog...");
|
||||
|
||||
CustomTestDialog dlg(this);
|
||||
int result = dlg.ShowModal();
|
||||
|
||||
if (result == wxID_OK) {
|
||||
LogEvent(wxString::Format("Custom dialog OK - value: '%s'", dlg.GetValue()));
|
||||
} else {
|
||||
LogEvent("Custom dialog cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
void DialogTestFrame::OnInputDialog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Input dialog...");
|
||||
|
||||
wxTextEntryDialog dlg(this, "Enter a component reference:",
|
||||
"Input Dialog", "R1");
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
LogEvent(wxString::Format("Input dialog OK - value: '%s'", dlg.GetValue()));
|
||||
} else {
|
||||
LogEvent("Input dialog cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
// Custom dialog implementation
|
||||
CustomTestDialog::CustomTestDialog(wxWindow* parent)
|
||||
: wxDialog(parent, wxID_ANY, "Custom Test Dialog",
|
||||
wxDefaultPosition, wxSize(300, 200))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* label = new wxStaticText(this, wxID_ANY,
|
||||
"This is a custom wxDialog\nlike KiCad's property dialogs:");
|
||||
mainSizer->Add(label, 0, wxALL, 10);
|
||||
|
||||
m_textCtrl = new wxTextCtrl(this, wxID_ANY, "Sample value");
|
||||
mainSizer->Add(m_textCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
}
|
||||
232
tests/apps/standalone/dnd/dnd_test.cpp
Normal file
232
tests/apps/standalone/dnd/dnd_test.cpp
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// wxDragDrop Test - Tests HTML5 file drop in WASM
|
||||
// KiCad uses drag and drop for loading projects, schematics, PCBs, etc.
|
||||
//
|
||||
// Tests:
|
||||
// - External file drops via HTML5 drag and drop API
|
||||
// - wxDropFilesEvent generation
|
||||
// - Multiple file drops
|
||||
// - Visual drop zone feedback
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/dnd.h"
|
||||
#include "wx/listbox.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
// Forward declarations
|
||||
class DndTestApp;
|
||||
class DndTestFrame;
|
||||
|
||||
// ============================================================
|
||||
// DndTestApp
|
||||
// ============================================================
|
||||
class DndTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
bool OnInit() override;
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// DndTestFrame - Main test window
|
||||
// ============================================================
|
||||
class DndTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
DndTestFrame();
|
||||
|
||||
private:
|
||||
wxPanel* m_dropZone;
|
||||
wxTextCtrl* m_log;
|
||||
wxListBox* m_fileList;
|
||||
bool m_dragOver;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void OnDropFiles(wxDropFilesEvent& evt);
|
||||
void OnDropZonePaint(wxPaintEvent& evt);
|
||||
void OnClearFiles(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// IDs
|
||||
enum {
|
||||
ID_DROP_ZONE = wxID_HIGHEST + 1,
|
||||
ID_CLEAR_FILES
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(DndTestFrame, wxFrame)
|
||||
EVT_DROP_FILES(DndTestFrame::OnDropFiles)
|
||||
EVT_BUTTON(ID_CLEAR_FILES, DndTestFrame::OnClearFiles)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(DndTestApp);
|
||||
|
||||
// ============================================================
|
||||
// App Implementation
|
||||
// ============================================================
|
||||
|
||||
bool DndTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
DndTestFrame* frame = new DndTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Frame Implementation
|
||||
// ============================================================
|
||||
|
||||
DndTestFrame::DndTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxDragDrop WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 600)),
|
||||
m_dragOver(false)
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxDragDrop Test\n\n"
|
||||
"KiCad uses drag and drop for loading projects, schematics, and PCBs.\n"
|
||||
"Test by dragging files from your file manager onto the drop zone below.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Accepted file types info
|
||||
wxStaticText* fileTypes = new wxStaticText(this, wxID_ANY,
|
||||
"Accepted file types: .kicad_pcb, .kicad_sch, .kicad_pro, .dxf, .svg, .png, .jpg, .txt, *.*");
|
||||
fileTypes->SetForegroundColour(*wxBLUE);
|
||||
mainSizer->Add(fileTypes, 0, wxLEFT | wxRIGHT, 10);
|
||||
|
||||
// Drop zone panel
|
||||
wxStaticBoxSizer* dropBox = new wxStaticBoxSizer(wxVERTICAL, this, "Drop Zone");
|
||||
m_dropZone = new wxPanel(this, ID_DROP_ZONE, wxDefaultPosition, wxSize(-1, 150));
|
||||
m_dropZone->SetBackgroundColour(wxColour(240, 240, 240));
|
||||
m_dropZone->Bind(wxEVT_PAINT, &DndTestFrame::OnDropZonePaint, this);
|
||||
dropBox->Add(m_dropZone, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(dropBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Enable drop target on the frame (wxWidgets will handle EVT_DROP_FILES)
|
||||
DragAcceptFiles(true);
|
||||
|
||||
// File list
|
||||
wxStaticBoxSizer* fileBox = new wxStaticBoxSizer(wxVERTICAL, this, "Dropped Files");
|
||||
m_fileList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100));
|
||||
fileBox->Add(m_fileList, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxButton* clearBtn = new wxButton(this, ID_CLEAR_FILES, "Clear Files");
|
||||
fileBox->Add(clearBtn, 0, wxALIGN_RIGHT | wxALL, 5);
|
||||
|
||||
mainSizer->Add(fileBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - Drag files here");
|
||||
|
||||
LogEvent("DragDrop test app started");
|
||||
LogEvent("DragAcceptFiles enabled on frame");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DND_TEST] wxDragDrop test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void DndTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
if (m_log)
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DND_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void DndTestFrame::OnDropZonePaint(wxPaintEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxPaintDC dc(m_dropZone);
|
||||
|
||||
// Draw background based on drag state
|
||||
if (m_dragOver) {
|
||||
dc.SetBrush(wxBrush(wxColour(200, 230, 255)));
|
||||
dc.SetPen(wxPen(wxColour(0, 120, 200), 2, wxPENSTYLE_DOT));
|
||||
} else {
|
||||
dc.SetBrush(wxBrush(wxColour(240, 240, 240)));
|
||||
dc.SetPen(wxPen(wxColour(180, 180, 180), 2, wxPENSTYLE_DOT));
|
||||
}
|
||||
|
||||
wxSize sz = m_dropZone->GetClientSize();
|
||||
dc.DrawRectangle(0, 0, sz.GetWidth(), sz.GetHeight());
|
||||
|
||||
// Draw text
|
||||
dc.SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc.SetTextForeground(m_dragOver ? wxColour(0, 100, 180) : wxColour(100, 100, 100));
|
||||
|
||||
wxString text = m_dragOver ? "Release to drop files" : "Drag files here";
|
||||
wxSize textSize = dc.GetTextExtent(text);
|
||||
dc.DrawText(text, (sz.GetWidth() - textSize.GetWidth()) / 2,
|
||||
(sz.GetHeight() - textSize.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
void DndTestFrame::OnDropFiles(wxDropFilesEvent& evt)
|
||||
{
|
||||
LogEvent("=== wxDropFilesEvent received! ===");
|
||||
|
||||
int numFiles = evt.GetNumberOfFiles();
|
||||
wxString* files = evt.GetFiles();
|
||||
|
||||
LogEvent(wxString::Format("Number of files: %d", numFiles));
|
||||
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
wxString filePath = files[i];
|
||||
LogEvent(wxString::Format("File %d: %s", i + 1, filePath));
|
||||
|
||||
// Add to file list
|
||||
m_fileList->Append(filePath);
|
||||
|
||||
// Try to read file info
|
||||
if (wxFileExists(filePath)) {
|
||||
wxFile file(filePath);
|
||||
if (file.IsOpened()) {
|
||||
wxFileOffset size = file.Length();
|
||||
LogEvent(wxString::Format(" Size: %lld bytes", (long long)size));
|
||||
file.Close();
|
||||
}
|
||||
} else {
|
||||
LogEvent(wxString::Format(" (File not found in WASM filesystem)"));
|
||||
}
|
||||
}
|
||||
|
||||
LogEvent("=== Drop complete ===");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[DND_EVENT] Drop complete: ' + $0 + ' files');
|
||||
}, numFiles);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DndTestFrame::OnClearFiles(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_fileList->Clear();
|
||||
LogEvent("File list cleared");
|
||||
}
|
||||
154
tests/apps/standalone/earlysize/earlysize_test.cpp
Normal file
154
tests/apps/standalone/earlysize/earlysize_test.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
// Early Size Test - Reproduces KiCad's pattern of calling GetClientSize() before Show()
|
||||
// This tests whether wxWidgets WASM port returns correct sizes during frame construction.
|
||||
//
|
||||
// KiCad's EDA_BASE_FRAME::commonInit() does:
|
||||
// m_frameSize = defaultSize(); // 1280x720
|
||||
// GetClientSize(&m_frameSize.x, &m_frameSize.y); // Overwrites with actual client size
|
||||
//
|
||||
// In WASM, GetClientSize() returns 20x20 if called before the frame is shown/sized.
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/display.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class EarlySizeTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class EarlySizeTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
EarlySizeTestFrame();
|
||||
|
||||
private:
|
||||
wxSize m_earlyClientSize; // Captured before Show()
|
||||
wxSize m_earlyFrameSize;
|
||||
wxStaticText* m_resultLabel;
|
||||
|
||||
void OnPaint(wxPaintEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(EarlySizeTestFrame, wxFrame)
|
||||
EVT_PAINT(EarlySizeTestFrame::OnPaint)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(EarlySizeTestApp);
|
||||
|
||||
bool EarlySizeTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
// Create frame with wxDefaultSize (like KiCad does)
|
||||
EarlySizeTestFrame* frame = new EarlySizeTestFrame();
|
||||
|
||||
// Show the frame (this is where sizing should happen)
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
EarlySizeTestFrame::EarlySizeTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "Early Size Test",
|
||||
wxDefaultPosition, wxDefaultSize) // No explicit size!
|
||||
{
|
||||
// === THIS IS THE KEY TEST ===
|
||||
// KiCad calls GetClientSize() in commonInit(), BEFORE Show()
|
||||
// In WASM, this should NOT return 20x20
|
||||
|
||||
GetClientSize(&m_earlyClientSize.x, &m_earlyClientSize.y);
|
||||
m_earlyFrameSize = GetSize();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[EARLYSIZE_TEST] Constructor called - BEFORE Show()');
|
||||
console.log('[EARLYSIZE_TEST] Early client size: ' + $0 + 'x' + $1);
|
||||
console.log('[EARLYSIZE_TEST] Early frame size: ' + $2 + 'x' + $3);
|
||||
}, m_earlyClientSize.x, m_earlyClientSize.y,
|
||||
m_earlyFrameSize.x, m_earlyFrameSize.y);
|
||||
#endif
|
||||
|
||||
// Create UI
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"Early Size Test\n\n"
|
||||
"This test reproduces KiCad's pattern:\n"
|
||||
"- Frame created with wxDefaultSize\n"
|
||||
"- GetClientSize() called in constructor BEFORE Show()\n"
|
||||
"- Size should NOT be 20x20!");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Results
|
||||
wxStaticBoxSizer* resultBox = new wxStaticBoxSizer(wxVERTICAL, this, "Results (from constructor)");
|
||||
|
||||
wxString resultText = wxString::Format(
|
||||
"Early Client Size: %dx%d\nEarly Frame Size: %dx%d",
|
||||
m_earlyClientSize.x, m_earlyClientSize.y,
|
||||
m_earlyFrameSize.x, m_earlyFrameSize.y);
|
||||
m_resultLabel = new wxStaticText(this, wxID_ANY, resultText);
|
||||
resultBox->Add(m_resultLabel, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(resultBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
// Maximize like KiCad does
|
||||
Maximize(true);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[EARLYSIZE_TEST] Early size test app started');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void EarlySizeTestFrame::OnPaint(wxPaintEvent& evt)
|
||||
{
|
||||
evt.Skip();
|
||||
|
||||
// Log final result after first paint
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
logged = true;
|
||||
|
||||
wxSize currentClient;
|
||||
GetClientSize(¤tClient.x, ¤tClient.y);
|
||||
wxSize currentFrame = GetSize();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// The key assertion: early client size should be > 100
|
||||
// If it's 20x20, that's the bug!
|
||||
bool earlyClientOk = (m_earlyClientSize.x > 100 && m_earlyClientSize.y > 100);
|
||||
bool earlyFrameOk = (m_earlyFrameSize.x > 100 && m_earlyFrameSize.y > 100);
|
||||
|
||||
EM_ASM({
|
||||
console.log('[EARLYSIZE_TEST] After Show() - current client: ' + $0 + 'x' + $1);
|
||||
console.log('[EARLYSIZE_TEST] After Show() - current frame: ' + $2 + 'x' + $3);
|
||||
|
||||
if ($4 && $5) {
|
||||
console.log('[EARLYSIZE_TEST] PASS: Early sizes were reasonable');
|
||||
} else {
|
||||
console.error('[EARLYSIZE_TEST] FAIL: Early sizes were tiny!');
|
||||
console.error('[EARLYSIZE_TEST] Early client was: ' + $6 + 'x' + $7);
|
||||
console.error('[EARLYSIZE_TEST] Early frame was: ' + $8 + 'x' + $9);
|
||||
}
|
||||
}, currentClient.x, currentClient.y,
|
||||
currentFrame.x, currentFrame.y,
|
||||
earlyClientOk ? 1 : 0, earlyFrameOk ? 1 : 0,
|
||||
m_earlyClientSize.x, m_earlyClientSize.y,
|
||||
m_earlyFrameSize.x, m_earlyFrameSize.y);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
162
tests/apps/standalone/filedialog/filedialog_test.cpp
Normal file
162
tests/apps/standalone/filedialog/filedialog_test.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// wxFileDialog Test - Tests file dialog functionality in WASM
|
||||
// KiCad uses file dialogs for opening/saving schematics, PCBs, footprints
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/filedlg.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class FileDialogTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class FileDialogTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
FileDialogTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnOpenFile(wxCommandEvent& evt);
|
||||
void OnSaveFile(wxCommandEvent& evt);
|
||||
void OnOpenMultiple(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_OPEN_FILE = wxID_HIGHEST + 1,
|
||||
ID_SAVE_FILE,
|
||||
ID_OPEN_MULTIPLE
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(FileDialogTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_OPEN_FILE, FileDialogTestFrame::OnOpenFile)
|
||||
EVT_BUTTON(ID_SAVE_FILE, FileDialogTestFrame::OnSaveFile)
|
||||
EVT_BUTTON(ID_OPEN_MULTIPLE, FileDialogTestFrame::OnOpenMultiple)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(FileDialogTestApp);
|
||||
|
||||
bool FileDialogTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
FileDialogTestFrame* frame = new FileDialogTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileDialogTestFrame::FileDialogTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxFileDialog WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 400))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxFileDialog Test\n\n"
|
||||
"Tests file dialog operations that KiCad uses for open/save.\n"
|
||||
"Note: Browser file access is typically restricted.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
buttonSizer->Add(new wxButton(this, ID_OPEN_FILE, "Open File..."), 0, wxALL, 5);
|
||||
buttonSizer->Add(new wxButton(this, ID_SAVE_FILE, "Save File..."), 0, wxALL, 5);
|
||||
buttonSizer->Add(new wxButton(this, ID_OPEN_MULTIPLE, "Open Multiple..."), 0, wxALL, 5);
|
||||
mainSizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
LogEvent("FileDialog test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[FILEDIALOG_TEST] wxFileDialog test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileDialogTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[FILEDIALOG_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileDialogTestFrame::OnOpenFile(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening file dialog...");
|
||||
|
||||
wxFileDialog openDialog(this, "Open File", "", "",
|
||||
"All files (*.*)|*.*|KiCad files (*.kicad_*)|*.kicad_*",
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (openDialog.ShowModal() == wxID_OK) {
|
||||
wxString path = openDialog.GetPath();
|
||||
LogEvent(wxString::Format("Selected file: %s", path));
|
||||
} else {
|
||||
LogEvent("Open dialog cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialogTestFrame::OnSaveFile(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening save dialog...");
|
||||
|
||||
wxFileDialog saveDialog(this, "Save File", "", "untitled.txt",
|
||||
"Text files (*.txt)|*.txt|All files (*.*)|*.*",
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
|
||||
if (saveDialog.ShowModal() == wxID_OK) {
|
||||
wxString path = saveDialog.GetPath();
|
||||
LogEvent(wxString::Format("Save to: %s", path));
|
||||
} else {
|
||||
LogEvent("Save dialog cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialogTestFrame::OnOpenMultiple(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening multiple file dialog...");
|
||||
|
||||
wxFileDialog openDialog(this, "Open Multiple Files", "", "",
|
||||
"All files (*.*)|*.*",
|
||||
wxFD_OPEN | wxFD_MULTIPLE);
|
||||
|
||||
if (openDialog.ShowModal() == wxID_OK) {
|
||||
wxArrayString paths;
|
||||
openDialog.GetPaths(paths);
|
||||
LogEvent(wxString::Format("Selected %zu files:", paths.GetCount()));
|
||||
for (size_t i = 0; i < paths.GetCount(); i++) {
|
||||
LogEvent(wxString::Format(" %s", paths[i]));
|
||||
}
|
||||
} else {
|
||||
LogEvent("Multiple file dialog cancelled");
|
||||
}
|
||||
}
|
||||
250
tests/apps/standalone/fontenum/fontenum_test.cpp
Normal file
250
tests/apps/standalone/fontenum/fontenum_test.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// wxFontEnumerator Test - Tests Local Font Access API integration
|
||||
// Tests: wxFontEnumerator::EnumerateFacenames(), font listing, sample rendering
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/fontenum.h"
|
||||
#include "wx/listbox.h"
|
||||
|
||||
// Custom font enumerator that collects font names
|
||||
class FontCollector : public wxFontEnumerator
|
||||
{
|
||||
public:
|
||||
wxArrayString& GetFonts() { return m_fonts; }
|
||||
|
||||
protected:
|
||||
virtual bool OnFacename(const wxString& facename) override
|
||||
{
|
||||
m_fonts.Add(facename);
|
||||
return true; // Continue enumeration
|
||||
}
|
||||
|
||||
private:
|
||||
wxArrayString m_fonts;
|
||||
};
|
||||
|
||||
class FontEnumFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
FontEnumFrame() : wxFrame(nullptr, wxID_ANY, "wxFontEnumerator Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Left panel - font list
|
||||
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* listLabel = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"System Fonts (via Local Font Access API):");
|
||||
leftSizer->Add(listLabel, 0, wxALL, 5);
|
||||
|
||||
m_fontList = new wxListBox(mainPanel, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(300, -1));
|
||||
m_fontList->Bind(wxEVT_LISTBOX, &FontEnumFrame::OnFontSelected, this);
|
||||
leftSizer->Add(m_fontList, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
m_enumButton = new wxButton(mainPanel, wxID_ANY, "Enumerate Fonts");
|
||||
m_enumButton->Bind(wxEVT_BUTTON, &FontEnumFrame::OnEnumerateFonts, this);
|
||||
leftSizer->Add(m_enumButton, 0, wxALL, 5);
|
||||
|
||||
m_statusText = new wxStaticText(mainPanel, wxID_ANY, "Click 'Enumerate Fonts' to start");
|
||||
leftSizer->Add(m_statusText, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(leftSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Right panel - font preview
|
||||
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* previewLabel = new wxStaticText(mainPanel, wxID_ANY, "Font Preview:");
|
||||
rightSizer->Add(previewLabel, 0, wxALL, 5);
|
||||
|
||||
m_previewPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(-1, 200));
|
||||
m_previewPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_previewPanel->Bind(wxEVT_PAINT, &FontEnumFrame::OnPaintPreview, this);
|
||||
rightSizer->Add(m_previewPanel, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Sample text sizes
|
||||
wxStaticText* sizesLabel = new wxStaticText(mainPanel, wxID_ANY, "Sample Text at Different Sizes:");
|
||||
rightSizer->Add(sizesLabel, 0, wxALL, 5);
|
||||
|
||||
m_samplesPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(-1, 250));
|
||||
m_samplesPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_samplesPanel->Bind(wxEVT_PAINT, &FontEnumFrame::OnPaintSamples, this);
|
||||
rightSizer->Add(m_samplesPanel, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainSizer->Add(rightSizer, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Event log
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
|
||||
wxBoxSizer* frameSizer = new wxBoxSizer(wxVERTICAL);
|
||||
frameSizer->Add(mainPanel, 1, wxEXPAND);
|
||||
frameSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
SetSizer(frameSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Font Enumeration Test - Uses Local Font Access API");
|
||||
Log("Font enumeration test app started");
|
||||
Log("Note: Requires Chrome/Edge 103+ and user permission");
|
||||
|
||||
// Auto-enumerate fonts on startup for testing
|
||||
CallAfter([this]() {
|
||||
wxCommandEvent evt;
|
||||
OnEnumerateFonts(evt);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void OnEnumerateFonts(wxCommandEvent& event)
|
||||
{
|
||||
m_fontList->Clear();
|
||||
m_selectedFont.Clear();
|
||||
m_previewPanel->Refresh();
|
||||
m_samplesPanel->Refresh();
|
||||
|
||||
Log("Starting font enumeration...");
|
||||
m_statusText->SetLabel("Enumerating fonts...");
|
||||
|
||||
FontCollector collector;
|
||||
bool success = collector.EnumerateFacenames();
|
||||
|
||||
if (success)
|
||||
{
|
||||
wxArrayString& fonts = collector.GetFonts();
|
||||
int count = fonts.GetCount();
|
||||
|
||||
Log(wxString::Format("Found %d font families", count));
|
||||
m_statusText->SetLabel(wxString::Format("Found %d fonts", count));
|
||||
|
||||
for (size_t i = 0; i < fonts.GetCount(); i++)
|
||||
{
|
||||
m_fontList->Append(fonts[i]);
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
m_fontList->SetSelection(0);
|
||||
m_selectedFont = fonts[0];
|
||||
m_previewPanel->Refresh();
|
||||
m_samplesPanel->Refresh();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Font enumeration failed or permission denied");
|
||||
m_statusText->SetLabel("Enumeration failed - check console");
|
||||
|
||||
// Add a note about the API requirement
|
||||
m_fontList->Append("(Font enumeration unavailable)");
|
||||
m_fontList->Append("Requires:");
|
||||
m_fontList->Append("- Chrome/Edge 103+");
|
||||
m_fontList->Append("- User permission");
|
||||
}
|
||||
}
|
||||
|
||||
void OnFontSelected(wxCommandEvent& event)
|
||||
{
|
||||
int sel = m_fontList->GetSelection();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
m_selectedFont = m_fontList->GetString(sel);
|
||||
Log(wxString::Format("Selected font: %s", m_selectedFont));
|
||||
m_previewPanel->Refresh();
|
||||
m_samplesPanel->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void OnPaintPreview(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_previewPanel);
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
if (m_selectedFont.IsEmpty())
|
||||
{
|
||||
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
|
||||
dc.SetTextForeground(wxColour(128, 128, 128));
|
||||
dc.DrawText("Select a font to preview", 10, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw font name and sample text
|
||||
wxFont font(24, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
|
||||
false, m_selectedFont);
|
||||
dc.SetFont(font);
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
|
||||
int y = 10;
|
||||
dc.DrawText(m_selectedFont, 10, y);
|
||||
y += 40;
|
||||
|
||||
dc.DrawText("The quick brown fox jumps over the lazy dog", 10, y);
|
||||
y += 40;
|
||||
|
||||
dc.DrawText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10, y);
|
||||
y += 40;
|
||||
|
||||
dc.DrawText("abcdefghijklmnopqrstuvwxyz", 10, y);
|
||||
y += 40;
|
||||
|
||||
dc.DrawText("0123456789 !@#$%^&*()", 10, y);
|
||||
}
|
||||
|
||||
void OnPaintSamples(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_samplesPanel);
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
if (m_selectedFont.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int sizes[] = {8, 10, 12, 14, 16, 18, 24, 32};
|
||||
int y = 10;
|
||||
|
||||
for (int size : sizes)
|
||||
{
|
||||
wxFont font(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
|
||||
false, m_selectedFont);
|
||||
dc.SetFont(font);
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
|
||||
wxString sample = wxString::Format("%dpt: Sample Text AaBbCc 123", size);
|
||||
dc.DrawText(sample, 10, y);
|
||||
y += size + 8;
|
||||
}
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxListBox* m_fontList;
|
||||
wxButton* m_enumButton;
|
||||
wxStaticText* m_statusText;
|
||||
wxPanel* m_previewPanel;
|
||||
wxPanel* m_samplesPanel;
|
||||
wxTextCtrl* m_log;
|
||||
wxString m_selectedFont;
|
||||
};
|
||||
|
||||
class FontEnumApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
FontEnumFrame* frame = new FontEnumFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(FontEnumApp);
|
||||
219
tests/apps/standalone/grid/grid_test.cpp
Normal file
219
tests/apps/standalone/grid/grid_test.cpp
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// Minimal wxGrid Test - Tests if wxGrid works in WASM
|
||||
// This is a SEPARATE test app because wxGrid may crash the app at startup
|
||||
// If this page loads successfully, wxGrid is working!
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/grid.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class GridTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class GridTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
GridTestFrame();
|
||||
|
||||
private:
|
||||
wxGrid* m_grid;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
// Grid event handlers
|
||||
void OnGridCellSelect(wxGridEvent& evt);
|
||||
void OnGridCellChange(wxGridEvent& evt);
|
||||
void OnGridLabelClick(wxGridEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_GRID = wxID_HIGHEST + 1,
|
||||
ID_LOG
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(GridTestFrame, wxFrame)
|
||||
EVT_GRID_SELECT_CELL(GridTestFrame::OnGridCellSelect)
|
||||
EVT_GRID_CELL_CHANGED(GridTestFrame::OnGridCellChange)
|
||||
EVT_GRID_LABEL_LEFT_CLICK(GridTestFrame::OnGridLabelClick)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(GridTestApp);
|
||||
|
||||
bool GridTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
GridTestFrame* frame = new GridTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
GridTestFrame::GridTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxGrid WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 500))
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 1. GridTestFrame constructor start'); });
|
||||
#endif
|
||||
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 2. BoxSizer created'); });
|
||||
#endif
|
||||
|
||||
// Status message
|
||||
wxStaticText* status = new wxStaticText(this, wxID_ANY,
|
||||
"SUCCESS: wxGrid initialized! If you see this, wxGrid is working in WASM.");
|
||||
status->SetForegroundColour(*wxGREEN);
|
||||
mainSizer->Add(status, 0, wxALL, 10);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 3. StaticText created'); });
|
||||
#endif
|
||||
|
||||
// Create wxGrid - THIS IS THE CRITICAL TEST
|
||||
// If wxGrid doesn't work in WASM, the app will crash HERE
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 4. About to create wxGrid...'); });
|
||||
#endif
|
||||
m_grid = new wxGrid(this, ID_GRID, wxDefaultPosition, wxSize(500, 200));
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 5. wxGrid created successfully!'); });
|
||||
#endif
|
||||
|
||||
// Setup grid with sample data (like KiCad's property grids)
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 6. About to call CreateGrid(5, 4)...'); });
|
||||
#endif
|
||||
m_grid->CreateGrid(5, 4);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[GRID_DEBUG] 7. CreateGrid completed!'); });
|
||||
#endif
|
||||
|
||||
// Set column labels (similar to KiCad's property dialogs)
|
||||
m_grid->SetColLabelValue(0, "Property");
|
||||
m_grid->SetColLabelValue(1, "Value");
|
||||
m_grid->SetColLabelValue(2, "Units");
|
||||
m_grid->SetColLabelValue(3, "Description");
|
||||
|
||||
// Set column widths
|
||||
m_grid->SetColSize(0, 100);
|
||||
m_grid->SetColSize(1, 80);
|
||||
m_grid->SetColSize(2, 50);
|
||||
m_grid->SetColSize(3, 150);
|
||||
|
||||
// Fill with sample data (like KiCad track/via properties)
|
||||
m_grid->SetCellValue(0, 0, "Track Width");
|
||||
m_grid->SetCellValue(0, 1, "0.25");
|
||||
m_grid->SetCellValue(0, 2, "mm");
|
||||
m_grid->SetCellValue(0, 3, "Default track width");
|
||||
|
||||
m_grid->SetCellValue(1, 0, "Via Size");
|
||||
m_grid->SetCellValue(1, 1, "0.80");
|
||||
m_grid->SetCellValue(1, 2, "mm");
|
||||
m_grid->SetCellValue(1, 3, "Via outer diameter");
|
||||
|
||||
m_grid->SetCellValue(2, 0, "Via Drill");
|
||||
m_grid->SetCellValue(2, 1, "0.40");
|
||||
m_grid->SetCellValue(2, 2, "mm");
|
||||
m_grid->SetCellValue(2, 3, "Via drill diameter");
|
||||
|
||||
m_grid->SetCellValue(3, 0, "Clearance");
|
||||
m_grid->SetCellValue(3, 1, "0.20");
|
||||
m_grid->SetCellValue(3, 2, "mm");
|
||||
m_grid->SetCellValue(3, 3, "Min clearance");
|
||||
|
||||
m_grid->SetCellValue(4, 0, "Net Class");
|
||||
m_grid->SetCellValue(4, 1, "Default");
|
||||
m_grid->SetCellValue(4, 2, "-");
|
||||
m_grid->SetCellValue(4, 3, "Net class name");
|
||||
|
||||
// Make first column read-only (like property names in KiCad)
|
||||
for (int row = 0; row < 5; row++) {
|
||||
m_grid->SetReadOnly(row, 0);
|
||||
m_grid->SetReadOnly(row, 2);
|
||||
m_grid->SetReadOnly(row, 3);
|
||||
}
|
||||
|
||||
mainSizer->Add(m_grid, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBox* logBox = new wxStaticBox(this, wxID_ANY, "Event Log");
|
||||
wxStaticBoxSizer* logSizer = new wxStaticBoxSizer(logBox, wxVERTICAL);
|
||||
|
||||
m_log = new wxTextCtrl(this, ID_LOG, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
logSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainSizer->Add(logSizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
LogEvent("wxGrid initialized successfully!");
|
||||
LogEvent("Try clicking cells, editing values, etc.");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[GRID_TEST] wxGrid test app started successfully!');
|
||||
console.log('[GRID_TEST] If you see this message, wxGrid is working in WASM!');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
// Guard against events firing before m_log is initialized
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[GRID_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridTestFrame::OnGridCellSelect(wxGridEvent& evt)
|
||||
{
|
||||
LogEvent(wxString::Format("Cell selected: row=%d, col=%d, value='%s'",
|
||||
evt.GetRow(), evt.GetCol(),
|
||||
m_grid->GetCellValue(evt.GetRow(), evt.GetCol())));
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void GridTestFrame::OnGridCellChange(wxGridEvent& evt)
|
||||
{
|
||||
LogEvent(wxString::Format("Cell changed: row=%d, col=%d, new value='%s'",
|
||||
evt.GetRow(), evt.GetCol(),
|
||||
m_grid->GetCellValue(evt.GetRow(), evt.GetCol())));
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void GridTestFrame::OnGridLabelClick(wxGridEvent& evt)
|
||||
{
|
||||
if (evt.GetRow() >= 0) {
|
||||
LogEvent(wxString::Format("Row label clicked: row=%d", evt.GetRow()));
|
||||
} else if (evt.GetCol() >= 0) {
|
||||
LogEvent(wxString::Format("Column label clicked: col=%d ('%s')",
|
||||
evt.GetCol(), m_grid->GetColLabelValue(evt.GetCol())));
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
206
tests/apps/standalone/gridedit/gridedit_test.cpp
Normal file
206
tests/apps/standalone/gridedit/gridedit_test.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
// wxGrid Cell Editing Test - Property editing simulation
|
||||
// Tests wxGrid with editable cells for KiCad property panels
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/grid.h"
|
||||
|
||||
class GridEditFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
GridEditFrame() : wxFrame(nullptr, wxID_ANY, "wxGrid Cell Editing Test",
|
||||
wxDefaultPosition, wxSize(900, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxGrid for editable property tables.\n"
|
||||
"Tests cell editing with different editor types: text, number, choice, checkbox.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Controls
|
||||
wxBoxSizer* controlSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btnAdd = new wxButton(mainPanel, wxID_ANY, "Add Row");
|
||||
wxButton* btnDelete = new wxButton(mainPanel, wxID_ANY, "Delete Row");
|
||||
wxButton* btnClear = new wxButton(mainPanel, wxID_ANY, "Clear Selection");
|
||||
|
||||
btnAdd->Bind(wxEVT_BUTTON, &GridEditFrame::OnAddRow, this);
|
||||
btnDelete->Bind(wxEVT_BUTTON, &GridEditFrame::OnDeleteRow, this);
|
||||
btnClear->Bind(wxEVT_BUTTON, &GridEditFrame::OnClearSelection, this);
|
||||
|
||||
controlSizer->Add(btnAdd, 0, wxRIGHT, 5);
|
||||
controlSizer->Add(btnDelete, 0, wxRIGHT, 5);
|
||||
controlSizer->Add(btnClear, 0);
|
||||
|
||||
mainSizer->Add(controlSizer, 0, wxALL, 5);
|
||||
|
||||
// Create grid
|
||||
m_grid = new wxGrid(mainPanel, wxID_ANY);
|
||||
m_grid->CreateGrid(10, 5);
|
||||
|
||||
// Set column headers
|
||||
m_grid->SetColLabelValue(0, "Reference");
|
||||
m_grid->SetColLabelValue(1, "Value");
|
||||
m_grid->SetColLabelValue(2, "Footprint");
|
||||
m_grid->SetColLabelValue(3, "Quantity");
|
||||
m_grid->SetColLabelValue(4, "DNP");
|
||||
|
||||
// Set column widths
|
||||
m_grid->SetColSize(0, 100);
|
||||
m_grid->SetColSize(1, 120);
|
||||
m_grid->SetColSize(2, 200);
|
||||
m_grid->SetColSize(3, 80);
|
||||
m_grid->SetColSize(4, 60);
|
||||
|
||||
// Set up cell editors
|
||||
// Column 0-2: Text editors (default)
|
||||
// Column 3: Number editor
|
||||
m_grid->SetColFormatNumber(3);
|
||||
|
||||
// Column 4: Boolean editor (checkbox)
|
||||
m_grid->SetColFormatBool(4);
|
||||
|
||||
// Populate with KiCad-like component data
|
||||
PopulateGrid();
|
||||
|
||||
// Set up choice editor for footprint column
|
||||
wxString footprints[] = {
|
||||
"Resistor_SMD:R_0402", "Resistor_SMD:R_0603", "Resistor_SMD:R_0805",
|
||||
"Capacitor_SMD:C_0402", "Capacitor_SMD:C_0603", "Capacitor_SMD:C_0805",
|
||||
"Package_QFP:LQFP-48", "Package_QFP:LQFP-64", "Package_QFP:LQFP-100"
|
||||
};
|
||||
wxGridCellChoiceEditor* choiceEditor = new wxGridCellChoiceEditor(9, footprints);
|
||||
m_grid->SetColAttr(2, new wxGridCellAttr());
|
||||
m_grid->GetOrCreateCellAttr(0, 2)->SetEditor(choiceEditor);
|
||||
|
||||
mainSizer->Add(m_grid, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Bind grid events
|
||||
m_grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridEditFrame::OnCellChanged, this);
|
||||
m_grid->Bind(wxEVT_GRID_SELECT_CELL, &GridEditFrame::OnCellSelected, this);
|
||||
m_grid->Bind(wxEVT_GRID_EDITOR_SHOWN, &GridEditFrame::OnEditorShown, this);
|
||||
m_grid->Bind(wxEVT_GRID_EDITOR_HIDDEN, &GridEditFrame::OnEditorHidden, this);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("Grid editing test app started");
|
||||
|
||||
Log("Grid editing test app started");
|
||||
Log("Double-click cells to edit. Use choice dropdown for Footprint column.");
|
||||
}
|
||||
|
||||
private:
|
||||
void PopulateGrid()
|
||||
{
|
||||
// Reference, Value, Footprint, Quantity, DNP
|
||||
SetRow(0, "R1", "10k", "Resistor_SMD:R_0402", "1", "0");
|
||||
SetRow(1, "R2", "4.7k", "Resistor_SMD:R_0402", "1", "0");
|
||||
SetRow(2, "R3", "100", "Resistor_SMD:R_0603", "2", "0");
|
||||
SetRow(3, "C1", "100nF", "Capacitor_SMD:C_0402", "1", "0");
|
||||
SetRow(4, "C2", "10uF", "Capacitor_SMD:C_0805", "1", "0");
|
||||
SetRow(5, "U1", "STM32F103", "Package_QFP:LQFP-48", "1", "0");
|
||||
SetRow(6, "U2", "74HC595", "Package_QFP:LQFP-64", "1", "1");
|
||||
SetRow(7, "J1", "USB-C", "Connector_USB:USB_C", "1", "0");
|
||||
SetRow(8, "D1", "LED_Red", "LED_SMD:LED_0603", "1", "0");
|
||||
SetRow(9, "Q1", "2N7002", "Package_TO:SOT-23", "1", "0");
|
||||
}
|
||||
|
||||
void SetRow(int row, const wxString& ref, const wxString& val,
|
||||
const wxString& fp, const wxString& qty, const wxString& dnp)
|
||||
{
|
||||
m_grid->SetCellValue(row, 0, ref);
|
||||
m_grid->SetCellValue(row, 1, val);
|
||||
m_grid->SetCellValue(row, 2, fp);
|
||||
m_grid->SetCellValue(row, 3, qty);
|
||||
m_grid->SetCellValue(row, 4, dnp);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void OnAddRow(wxCommandEvent& event)
|
||||
{
|
||||
int newRow = m_grid->GetNumberRows();
|
||||
m_grid->AppendRows(1);
|
||||
m_grid->SetCellValue(newRow, 0, wxString::Format("NEW%d", newRow + 1));
|
||||
m_grid->SetCellValue(newRow, 3, "1");
|
||||
m_grid->SetCellValue(newRow, 4, "0");
|
||||
Log(wxString::Format("Added row %d", newRow));
|
||||
}
|
||||
|
||||
void OnDeleteRow(wxCommandEvent& event)
|
||||
{
|
||||
int row = m_grid->GetGridCursorRow();
|
||||
if (row >= 0 && m_grid->GetNumberRows() > 1)
|
||||
{
|
||||
wxString ref = m_grid->GetCellValue(row, 0);
|
||||
m_grid->DeleteRows(row, 1);
|
||||
Log(wxString::Format("Deleted row: %s", ref));
|
||||
}
|
||||
}
|
||||
|
||||
void OnClearSelection(wxCommandEvent& event)
|
||||
{
|
||||
m_grid->ClearSelection();
|
||||
Log("Selection cleared");
|
||||
}
|
||||
|
||||
void OnCellChanged(wxGridEvent& event)
|
||||
{
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
wxString value = m_grid->GetCellValue(row, col);
|
||||
wxString colName = m_grid->GetColLabelValue(col);
|
||||
Log(wxString::Format("Cell changed: [%d,%d] %s = '%s'", row, col, colName, value));
|
||||
}
|
||||
|
||||
void OnCellSelected(wxGridEvent& event)
|
||||
{
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
Log(wxString::Format("Cell selected: [%d,%d]", row, col));
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void OnEditorShown(wxGridEvent& event)
|
||||
{
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
Log(wxString::Format("Editor opened: [%d,%d]", row, col));
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void OnEditorHidden(wxGridEvent& event)
|
||||
{
|
||||
Log("Editor closed");
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
wxGrid* m_grid;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class GridEditApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
GridEditFrame* frame = new GridEditFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(GridEditApp);
|
||||
330
tests/apps/standalone/gridrenderers/gridrenderers_test.cpp
Normal file
330
tests/apps/standalone/gridrenderers/gridrenderers_test.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
// wxGrid Custom Cell Renderers Test - KiCad-style grid cells
|
||||
// Tests custom cell rendering: color cells, icon+text, checkboxes, striped rows
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/grid.h"
|
||||
#include "wx/dcmemory.h"
|
||||
#include "wx/notebook.h"
|
||||
|
||||
// Custom Color Cell Renderer - like KiCad's layer color swatches
|
||||
class ColorCellRenderer : public wxGridCellRenderer
|
||||
{
|
||||
public:
|
||||
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
|
||||
const wxRect& rect, int row, int col, bool isSelected) override
|
||||
{
|
||||
wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
|
||||
|
||||
wxString value = grid.GetCellValue(row, col);
|
||||
wxColour color;
|
||||
if (color.Set(value))
|
||||
{
|
||||
// Draw color swatch
|
||||
wxRect colorRect = rect;
|
||||
colorRect.Deflate(4);
|
||||
dc.SetBrush(wxBrush(color));
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.DrawRectangle(colorRect);
|
||||
}
|
||||
}
|
||||
|
||||
virtual wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
|
||||
int row, int col) override
|
||||
{
|
||||
return wxSize(60, 20);
|
||||
}
|
||||
|
||||
virtual wxGridCellRenderer* Clone() const override
|
||||
{
|
||||
return new ColorCellRenderer();
|
||||
}
|
||||
};
|
||||
|
||||
// Custom Icon+Text Renderer - like KiCad's footprint list with icons
|
||||
class IconTextRenderer : public wxGridCellStringRenderer
|
||||
{
|
||||
public:
|
||||
IconTextRenderer(const wxColour& iconColor = *wxBLUE) : m_iconColor(iconColor) {}
|
||||
|
||||
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
|
||||
const wxRect& rect, int row, int col, bool isSelected) override
|
||||
{
|
||||
// Draw background
|
||||
wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
|
||||
|
||||
// Draw icon (small colored square)
|
||||
wxRect iconRect(rect.x + 2, rect.y + 3, 14, 14);
|
||||
dc.SetBrush(wxBrush(m_iconColor));
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.DrawRectangle(iconRect);
|
||||
|
||||
// Draw text offset by icon width
|
||||
wxRect textRect = rect;
|
||||
textRect.x += 20;
|
||||
textRect.width -= 20;
|
||||
|
||||
dc.SetTextForeground(isSelected ? *wxWHITE : *wxBLACK);
|
||||
dc.DrawLabel(grid.GetCellValue(row, col), textRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
|
||||
virtual wxGridCellRenderer* Clone() const override
|
||||
{
|
||||
return new IconTextRenderer(m_iconColor);
|
||||
}
|
||||
|
||||
private:
|
||||
wxColour m_iconColor;
|
||||
};
|
||||
|
||||
// Striped Row Renderer - alternating row colors like KiCad's symbol editor
|
||||
class StripedRenderer : public wxGridCellStringRenderer
|
||||
{
|
||||
public:
|
||||
virtual void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
|
||||
const wxRect& rect, int row, int col, bool isSelected) override
|
||||
{
|
||||
// Alternating background colors
|
||||
if (!isSelected)
|
||||
{
|
||||
wxColour bgColor = (row % 2 == 0) ? wxColour(255, 255, 255) : wxColour(240, 240, 245);
|
||||
dc.SetBrush(wxBrush(bgColor));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect);
|
||||
}
|
||||
|
||||
// Draw text
|
||||
wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
|
||||
}
|
||||
|
||||
virtual wxGridCellRenderer* Clone() const override
|
||||
{
|
||||
return new StripedRenderer();
|
||||
}
|
||||
};
|
||||
|
||||
class GridRenderersFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
GridRenderersFrame() : wxFrame(nullptr, wxID_ANY, "wxGrid Custom Cell Renderers Test",
|
||||
wxDefaultPosition, wxSize(1000, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses custom grid cell renderers for color swatches, icons, and striped rows.\n"
|
||||
"Tests: Color cells, Icon+Text cells, Striped rows, Checkbox cells.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Create notebook with different grid examples
|
||||
m_notebook = new wxNotebook(mainPanel, wxID_ANY);
|
||||
|
||||
// Tab 1: Color Cells (like KiCad layer manager)
|
||||
CreateColorGrid();
|
||||
|
||||
// Tab 2: Icon + Text (like footprint browser)
|
||||
CreateIconTextGrid();
|
||||
|
||||
// Tab 3: Striped rows with checkboxes
|
||||
CreateStripedGrid();
|
||||
|
||||
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Grid renderers test app started");
|
||||
Log("Grid renderers test app started");
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateColorGrid()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxGrid* grid = new wxGrid(panel, wxID_ANY);
|
||||
grid->CreateGrid(8, 3);
|
||||
|
||||
grid->SetColLabelValue(0, "Layer");
|
||||
grid->SetColLabelValue(1, "Color");
|
||||
grid->SetColLabelValue(2, "Visible");
|
||||
|
||||
grid->SetColSize(0, 150);
|
||||
grid->SetColSize(1, 100);
|
||||
grid->SetColSize(2, 80);
|
||||
|
||||
// Set up color renderer for column 1 (read-only display)
|
||||
wxGridCellAttr* colorAttr = new wxGridCellAttr();
|
||||
colorAttr->SetRenderer(new ColorCellRenderer());
|
||||
colorAttr->SetReadOnly(true); // Display only - would need dialog to edit
|
||||
grid->SetColAttr(1, colorAttr);
|
||||
|
||||
// Boolean column
|
||||
grid->SetColFormatBool(2);
|
||||
|
||||
// Layer data
|
||||
const char* layers[] = {"F.Cu", "B.Cu", "F.SilkS", "B.SilkS", "F.Mask", "B.Mask", "Edge.Cuts", "Dwgs.User"};
|
||||
const char* colors[] = {"#FF0000", "#0000FF", "#FFFF00", "#FF00FF", "#00FF00", "#00FFFF", "#FFFFFF", "#808080"};
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
grid->SetCellValue(i, 0, layers[i]);
|
||||
grid->SetCellValue(i, 1, colors[i]);
|
||||
grid->SetCellValue(i, 2, "1");
|
||||
}
|
||||
|
||||
grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridRenderersFrame::OnCellChanged, this);
|
||||
|
||||
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Color Cells");
|
||||
m_colorGrid = grid;
|
||||
}
|
||||
|
||||
void CreateIconTextGrid()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxGrid* grid = new wxGrid(panel, wxID_ANY);
|
||||
grid->CreateGrid(10, 3);
|
||||
|
||||
grid->SetColLabelValue(0, "Component");
|
||||
grid->SetColLabelValue(1, "Footprint");
|
||||
grid->SetColLabelValue(2, "Library");
|
||||
|
||||
grid->SetColSize(0, 180);
|
||||
grid->SetColSize(1, 250);
|
||||
grid->SetColSize(2, 150);
|
||||
|
||||
// Set up icon+text renderer
|
||||
wxGridCellAttr* iconAttr = new wxGridCellAttr();
|
||||
iconAttr->SetRenderer(new IconTextRenderer(*wxRED));
|
||||
grid->SetColAttr(0, iconAttr);
|
||||
|
||||
wxGridCellAttr* iconAttr2 = new wxGridCellAttr();
|
||||
iconAttr2->SetRenderer(new IconTextRenderer(wxColour(0, 128, 0)));
|
||||
grid->SetColAttr(1, iconAttr2);
|
||||
|
||||
// Data
|
||||
const char* components[] = {"R_0402", "C_0603", "LED_0805", "STM32F4", "USB_C",
|
||||
"MOSFET_SOT23", "LDO_SOT223", "Crystal_3225", "Header_2x5", "Connector_JST"};
|
||||
const char* footprints[] = {"Resistor_SMD:R_0402", "Capacitor_SMD:C_0603", "LED_SMD:LED_0805",
|
||||
"Package_QFP:LQFP-100", "Connector_USB:USB_C",
|
||||
"Package_TO:SOT-23", "Package_TO:SOT-223", "Crystal:Crystal_3225",
|
||||
"Connector_Pin:2x5", "Connector_JST:JST_PH_4"};
|
||||
const char* libraries[] = {"Resistor_SMD", "Capacitor_SMD", "LED_SMD", "Package_QFP",
|
||||
"Connector_USB", "Package_TO", "Package_TO", "Crystal",
|
||||
"Connector_Pin", "Connector_JST"};
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
grid->SetCellValue(i, 0, components[i]);
|
||||
grid->SetCellValue(i, 1, footprints[i]);
|
||||
grid->SetCellValue(i, 2, libraries[i]);
|
||||
}
|
||||
|
||||
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Icon+Text");
|
||||
m_iconGrid = grid;
|
||||
}
|
||||
|
||||
void CreateStripedGrid()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxGrid* grid = new wxGrid(panel, wxID_ANY);
|
||||
grid->CreateGrid(12, 4);
|
||||
|
||||
grid->SetColLabelValue(0, "Reference");
|
||||
grid->SetColLabelValue(1, "Value");
|
||||
grid->SetColLabelValue(2, "DNP");
|
||||
grid->SetColLabelValue(3, "Excluded");
|
||||
|
||||
grid->SetColSize(0, 100);
|
||||
grid->SetColSize(1, 150);
|
||||
grid->SetColSize(2, 80);
|
||||
grid->SetColSize(3, 80);
|
||||
|
||||
// Apply striped renderer to text columns
|
||||
wxGridCellAttr* stripedAttr = new wxGridCellAttr();
|
||||
stripedAttr->SetRenderer(new StripedRenderer());
|
||||
grid->SetColAttr(0, stripedAttr);
|
||||
|
||||
wxGridCellAttr* stripedAttr2 = new wxGridCellAttr();
|
||||
stripedAttr2->SetRenderer(new StripedRenderer());
|
||||
grid->SetColAttr(1, stripedAttr2);
|
||||
|
||||
// Boolean columns
|
||||
grid->SetColFormatBool(2);
|
||||
grid->SetColFormatBool(3);
|
||||
|
||||
// BOM-like data
|
||||
const char* refs[] = {"R1", "R2", "R3", "R4", "C1", "C2", "C3", "U1", "U2", "J1", "D1", "Q1"};
|
||||
const char* values[] = {"10k", "4.7k", "100", "1M", "100nF", "10uF", "22pF", "STM32F103",
|
||||
"74HC595", "USB-C", "LED_Red", "2N7002"};
|
||||
const char* dnp[] = {"0", "0", "1", "0", "0", "0", "0", "0", "1", "0", "0", "0"};
|
||||
const char* excluded[] = {"0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0"};
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
grid->SetCellValue(i, 0, refs[i]);
|
||||
grid->SetCellValue(i, 1, values[i]);
|
||||
grid->SetCellValue(i, 2, dnp[i]);
|
||||
grid->SetCellValue(i, 3, excluded[i]);
|
||||
}
|
||||
|
||||
grid->Bind(wxEVT_GRID_CELL_CHANGED, &GridRenderersFrame::OnCellChanged, this);
|
||||
|
||||
sizer->Add(grid, 1, wxEXPAND | wxALL, 5);
|
||||
panel->SetSizer(sizer);
|
||||
m_notebook->AddPage(panel, "Striped+Checkboxes");
|
||||
m_stripedGrid = grid;
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void OnCellChanged(wxGridEvent& event)
|
||||
{
|
||||
wxGrid* grid = dynamic_cast<wxGrid*>(event.GetEventObject());
|
||||
if (grid)
|
||||
{
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
wxString value = grid->GetCellValue(row, col);
|
||||
Log(wxString::Format("Cell [%d,%d] changed to: %s", row, col, value));
|
||||
}
|
||||
}
|
||||
|
||||
wxNotebook* m_notebook;
|
||||
wxGrid* m_colorGrid;
|
||||
wxGrid* m_iconGrid;
|
||||
wxGrid* m_stripedGrid;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class GridRenderersApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
GridRenderersFrame* frame = new GridRenderersFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(GridRenderersApp);
|
||||
346
tests/apps/standalone/htmlwin/htmlwin_test.cpp
Normal file
346
tests/apps/standalone/htmlwin/htmlwin_test.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
// wxHtmlWindow Test - Tests HTML Window in WASM
|
||||
// KiCad uses wxHtmlWindow for About dialogs, error formatting, and descriptions
|
||||
// instead of wxRichTextCtrl
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/html/htmlwin.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class HtmlWinTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class HtmlWinTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
HtmlWinTestFrame();
|
||||
|
||||
private:
|
||||
wxHtmlWindow* m_htmlWin;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void SetBasicContent();
|
||||
void SetTableContent();
|
||||
void SetLongContent();
|
||||
|
||||
void OnLinkClicked(wxHtmlLinkEvent& evt);
|
||||
void OnBasicContent(wxCommandEvent& evt);
|
||||
void OnTableContent(wxCommandEvent& evt);
|
||||
void OnLongContent(wxCommandEvent& evt);
|
||||
void OnKiCadAbout(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_HTML = wxID_HIGHEST + 1,
|
||||
ID_BASIC_CONTENT,
|
||||
ID_TABLE_CONTENT,
|
||||
ID_LONG_CONTENT,
|
||||
ID_KICAD_ABOUT
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(HtmlWinTestFrame, wxFrame)
|
||||
EVT_HTML_LINK_CLICKED(ID_HTML, HtmlWinTestFrame::OnLinkClicked)
|
||||
EVT_BUTTON(ID_BASIC_CONTENT, HtmlWinTestFrame::OnBasicContent)
|
||||
EVT_BUTTON(ID_TABLE_CONTENT, HtmlWinTestFrame::OnTableContent)
|
||||
EVT_BUTTON(ID_LONG_CONTENT, HtmlWinTestFrame::OnLongContent)
|
||||
EVT_BUTTON(ID_KICAD_ABOUT, HtmlWinTestFrame::OnKiCadAbout)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(HtmlWinTestApp);
|
||||
|
||||
bool HtmlWinTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
HtmlWinTestFrame* frame = new HtmlWinTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
HtmlWinTestFrame::HtmlWinTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxHtmlWindow WASM Test",
|
||||
wxDefaultPosition, wxSize(700, 650))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxHtmlWindow Test\n\n"
|
||||
"KiCad uses HtmlWindow for About dialogs, error messages, and symbol descriptions.\n"
|
||||
"Click buttons to load different HTML content.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Button bar
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_BASIC_CONTENT, "Basic HTML"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_TABLE_CONTENT, "Tables"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_LONG_CONTENT, "Long Content"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_KICAD_ABOUT, "KiCad-style About"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// HTML Window
|
||||
m_htmlWin = new wxHtmlWindow(this, ID_HTML, wxDefaultPosition, wxSize(-1, 300),
|
||||
wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER);
|
||||
mainSizer->Add(m_htmlWin, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
// Set initial content
|
||||
SetBasicContent();
|
||||
|
||||
LogEvent("HtmlWindow test app started");
|
||||
LogEvent("Initial content loaded");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[HTMLWIN_TEST] wxHtmlWindow test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[HTMLWIN_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::SetBasicContent()
|
||||
{
|
||||
wxString html = R"(
|
||||
<html>
|
||||
<body>
|
||||
<h1>Basic HTML Test</h1>
|
||||
<p>This tests <b>bold</b>, <i>italic</i>, and <u>underlined</u> text.</p>
|
||||
|
||||
<h2>Lists</h2>
|
||||
<ul>
|
||||
<li>Unordered item 1</li>
|
||||
<li>Unordered item 2</li>
|
||||
<li>Unordered item 3</li>
|
||||
</ul>
|
||||
|
||||
<ol>
|
||||
<li>Ordered item 1</li>
|
||||
<li>Ordered item 2</li>
|
||||
<li>Ordered item 3</li>
|
||||
</ol>
|
||||
|
||||
<h2>Links</h2>
|
||||
<p>Click this <a href="test://link1">test link</a> to fire an event.</p>
|
||||
<p>Another <a href="test://link2">second link</a> for testing.</p>
|
||||
|
||||
<h2>Colors</h2>
|
||||
<p><font color="red">Red text</font>,
|
||||
<font color="green">green text</font>,
|
||||
<font color="blue">blue text</font>.</p>
|
||||
|
||||
<h2>Horizontal Rule</h2>
|
||||
<hr>
|
||||
<p>Content below the line.</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
m_htmlWin->SetPage(html);
|
||||
LogEvent("Loaded basic HTML content");
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::SetTableContent()
|
||||
{
|
||||
wxString html = R"(
|
||||
<html>
|
||||
<body>
|
||||
<h1>Table Test</h1>
|
||||
<p>This tests HTML tables similar to KiCad's component info display.</p>
|
||||
|
||||
<h2>Component Properties</h2>
|
||||
<table border="1" cellpadding="5">
|
||||
<tr bgcolor="#CCCCCC">
|
||||
<th>Property</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reference</td>
|
||||
<td>U1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Value</td>
|
||||
<td>STM32F103C8</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Footprint</td>
|
||||
<td>LQFP-48</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Datasheet</td>
|
||||
<td><a href="test://datasheet">View PDF</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Pin Table</h2>
|
||||
<table border="1" cellpadding="3">
|
||||
<tr bgcolor="#E0E0E0">
|
||||
<th>Pin</th>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Net</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>VCC</td>
|
||||
<td>Power</td>
|
||||
<td>+3V3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>GND</td>
|
||||
<td>Power</td>
|
||||
<td>GND</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>PA0</td>
|
||||
<td>I/O</td>
|
||||
<td>Net1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>PA1</td>
|
||||
<td>I/O</td>
|
||||
<td>Net2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
m_htmlWin->SetPage(html);
|
||||
LogEvent("Loaded table HTML content");
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::SetLongContent()
|
||||
{
|
||||
wxString html = R"(<html><body>
|
||||
<h1>Long Scrollable Content</h1>
|
||||
<p>This tests scrolling behavior with long content.</p>
|
||||
)";
|
||||
|
||||
// Generate long content
|
||||
for (int i = 1; i <= 30; i++) {
|
||||
html += wxString::Format(
|
||||
"<h3>Section %d</h3>\n"
|
||||
"<p>This is paragraph %d of the long content test. "
|
||||
"It contains enough text to verify scrolling works correctly "
|
||||
"in the wxHtmlWindow WASM implementation.</p>\n",
|
||||
i, i
|
||||
);
|
||||
}
|
||||
|
||||
html += "</body></html>";
|
||||
m_htmlWin->SetPage(html);
|
||||
LogEvent("Loaded long scrollable content (30 sections)");
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::OnLinkClicked(wxHtmlLinkEvent& evt)
|
||||
{
|
||||
wxString href = evt.GetLinkInfo().GetHref();
|
||||
LogEvent(wxString::Format("Link clicked: '%s'", href));
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[HTMLWIN_LINK] Link clicked: ' + UTF8ToString($0));
|
||||
}, href.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::OnBasicContent(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetBasicContent();
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::OnTableContent(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetTableContent();
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::OnLongContent(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetLongContent();
|
||||
}
|
||||
|
||||
void HtmlWinTestFrame::OnKiCadAbout(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxString html = R"(
|
||||
<html>
|
||||
<body>
|
||||
<center>
|
||||
<h1>KiCad EDA</h1>
|
||||
<p><b>Version 8.0.0</b></p>
|
||||
<p>An open source EDA suite for schematic capture<br>
|
||||
and PCB design.</p>
|
||||
<hr width="50%">
|
||||
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td align="right"><b>Build:</b></td>
|
||||
<td>WASM (Emscripten)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right"><b>Platform:</b></td>
|
||||
<td>Web Browser</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right"><b>wxWidgets:</b></td>
|
||||
<td>3.3.0</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr width="50%">
|
||||
|
||||
<h3>Libraries</h3>
|
||||
<p>
|
||||
<a href="test://wxwidgets">wxWidgets</a> |
|
||||
<a href="test://boost">Boost</a> |
|
||||
<a href="test://opencascade">OpenCASCADE</a>
|
||||
</p>
|
||||
|
||||
<h3>License</h3>
|
||||
<p>KiCad is free software licensed under the<br>
|
||||
<a href="test://gpl">GNU General Public License v3</a></p>
|
||||
|
||||
<p><font size="-1">Copyright (c) 2024 KiCad Developers</font></p>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
m_htmlWin->SetPage(html);
|
||||
LogEvent("Loaded KiCad-style About content");
|
||||
}
|
||||
204
tests/apps/standalone/infobar/infobar_test.cpp
Normal file
204
tests/apps/standalone/infobar/infobar_test.cpp
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// wxInfoBar Test - Tests info bar in WASM
|
||||
// KiCad uses info bars for notifications and warnings
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/infobar.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class InfoBarTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class InfoBarTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
InfoBarTestFrame();
|
||||
|
||||
private:
|
||||
wxInfoBar* m_infoBar;
|
||||
wxTextCtrl* m_log;
|
||||
wxPanel* m_contentPanel;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnShowInfo(wxCommandEvent& evt);
|
||||
void OnShowWarning(wxCommandEvent& evt);
|
||||
void OnShowError(wxCommandEvent& evt);
|
||||
void OnShowWithButton(wxCommandEvent& evt);
|
||||
void OnDismiss(wxCommandEvent& evt);
|
||||
void OnInfoBarButton(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_SHOW_INFO = wxID_HIGHEST + 1,
|
||||
ID_SHOW_WARNING,
|
||||
ID_SHOW_ERROR,
|
||||
ID_SHOW_WITH_BUTTON,
|
||||
ID_DISMISS,
|
||||
ID_INFOBAR_BUTTON
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(InfoBarTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_SHOW_INFO, InfoBarTestFrame::OnShowInfo)
|
||||
EVT_BUTTON(ID_SHOW_WARNING, InfoBarTestFrame::OnShowWarning)
|
||||
EVT_BUTTON(ID_SHOW_ERROR, InfoBarTestFrame::OnShowError)
|
||||
EVT_BUTTON(ID_SHOW_WITH_BUTTON, InfoBarTestFrame::OnShowWithButton)
|
||||
EVT_BUTTON(ID_DISMISS, InfoBarTestFrame::OnDismiss)
|
||||
EVT_BUTTON(ID_INFOBAR_BUTTON, InfoBarTestFrame::OnInfoBarButton)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(InfoBarTestApp);
|
||||
|
||||
bool InfoBarTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
InfoBarTestFrame* frame = new InfoBarTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
InfoBarTestFrame::InfoBarTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxInfoBar WASM Test",
|
||||
wxDefaultPosition, wxSize(700, 500))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Info bar at the top (like KiCad)
|
||||
m_infoBar = new wxInfoBar(this);
|
||||
mainSizer->Add(m_infoBar, 0, wxEXPAND);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxInfoBar Test\n\n"
|
||||
"KiCad uses wxInfoBar for notifications, warnings, and error messages.\n"
|
||||
"Click the buttons below to show different types of messages.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Button panel
|
||||
wxStaticBoxSizer* btnBox = new wxStaticBoxSizer(wxVERTICAL, this, "Show Messages");
|
||||
|
||||
wxFlexGridSizer* btnGrid = new wxFlexGridSizer(2, 10, 10);
|
||||
|
||||
btnGrid->Add(new wxButton(this, ID_SHOW_INFO, "Show Info Message"), 0, wxEXPAND);
|
||||
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Blue info bar with info icon"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
btnGrid->Add(new wxButton(this, ID_SHOW_WARNING, "Show Warning Message"), 0, wxEXPAND);
|
||||
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Yellow warning bar with warning icon"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
btnGrid->Add(new wxButton(this, ID_SHOW_ERROR, "Show Error Message"), 0, wxEXPAND);
|
||||
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Red error bar with error icon"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
btnGrid->Add(new wxButton(this, ID_SHOW_WITH_BUTTON, "Show With Action Button"), 0, wxEXPAND);
|
||||
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Info bar with clickable action button"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
btnGrid->Add(new wxButton(this, ID_DISMISS, "Dismiss"), 0, wxEXPAND);
|
||||
btnGrid->Add(new wxStaticText(this, wxID_ANY, "Hide the info bar"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
btnBox->Add(btnGrid, 0, wxALL, 10);
|
||||
mainSizer->Add(btnBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Sample KiCad-like messages box
|
||||
wxStaticBoxSizer* exampleBox = new wxStaticBoxSizer(wxVERTICAL, this, "Example KiCad Messages");
|
||||
|
||||
wxArrayString examples;
|
||||
examples.Add("INFO: Board successfully loaded from 'myproject.kicad_pcb'");
|
||||
examples.Add("WARNING: Footprint 'R_0402' not found in library, using fallback");
|
||||
examples.Add("ERROR: DRC violation: Clearance 0.15mm < 0.2mm required");
|
||||
examples.Add("INFO: Design rules check completed: 0 errors, 2 warnings");
|
||||
examples.Add("WARNING: Component 'U3' has unconnected pins: 12, 14, 15");
|
||||
|
||||
wxListBox* exampleList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 100),
|
||||
examples, wxLB_SINGLE);
|
||||
exampleBox->Add(exampleList, 0, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(exampleBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - wxInfoBar test");
|
||||
|
||||
LogEvent("InfoBar test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[INFOBAR_TEST] wxInfoBar test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[INFOBAR_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnShowInfo(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_infoBar->ShowMessage("Board successfully loaded. Design contains 245 components and 89 nets.",
|
||||
wxICON_INFORMATION);
|
||||
LogEvent("Showed info message");
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnShowWarning(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_infoBar->ShowMessage("Warning: Some footprints could not be found in the configured libraries.",
|
||||
wxICON_WARNING);
|
||||
LogEvent("Showed warning message");
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnShowError(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_infoBar->ShowMessage("Error: DRC check failed. 3 clearance violations found.",
|
||||
wxICON_ERROR);
|
||||
LogEvent("Showed error message");
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnShowWithButton(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_infoBar->RemoveButton(ID_INFOBAR_BUTTON);
|
||||
m_infoBar->AddButton(ID_INFOBAR_BUTTON, "View Details");
|
||||
m_infoBar->ShowMessage("ERC completed with 2 warnings. Click to view details.",
|
||||
wxICON_WARNING);
|
||||
LogEvent("Showed message with action button");
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnDismiss(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_infoBar->Dismiss();
|
||||
LogEvent("Info bar dismissed");
|
||||
}
|
||||
|
||||
void InfoBarTestFrame::OnInfoBarButton(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Info bar action button clicked - would open details dialog");
|
||||
wxMessageBox("This would open the ERC results dialog in KiCad.",
|
||||
"Action Button Clicked", wxOK | wxICON_INFORMATION);
|
||||
}
|
||||
142
tests/apps/standalone/layout/layout_test.cpp
Normal file
142
tests/apps/standalone/layout/layout_test.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// Layout Test - Tests wxSplitterWindow and wxScrolledWindow in WASM
|
||||
// KiCad uses splitters and scrolled windows extensively
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/splitter.h"
|
||||
#include "wx/scrolwin.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class LayoutTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class LayoutTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
LayoutTestFrame();
|
||||
|
||||
private:
|
||||
wxSplitterWindow* m_splitter;
|
||||
wxScrolledWindow* m_scrollLeft;
|
||||
wxScrolledWindow* m_scrollRight;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void OnSplitterSashPosChanged(wxSplitterEvent& evt);
|
||||
void OnScrollWin(wxScrollWinEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(LayoutTestFrame, wxFrame)
|
||||
EVT_SPLITTER_SASH_POS_CHANGED(wxID_ANY, LayoutTestFrame::OnSplitterSashPosChanged)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(LayoutTestApp);
|
||||
|
||||
bool LayoutTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
LayoutTestFrame* frame = new LayoutTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
LayoutTestFrame::LayoutTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxSplitter/wxScrolled WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxSplitterWindow and wxScrolledWindow Test - KiCad layout controls");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Create splitter
|
||||
m_splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition,
|
||||
wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE);
|
||||
|
||||
// Left scrolled window
|
||||
m_scrollLeft = new wxScrolledWindow(m_splitter, wxID_ANY);
|
||||
m_scrollLeft->SetBackgroundColour(*wxLIGHT_GREY);
|
||||
m_scrollLeft->SetScrollbars(10, 10, 100, 100);
|
||||
|
||||
wxBoxSizer* leftSizer = new wxBoxSizer(wxVERTICAL);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
leftSizer->Add(new wxStaticText(m_scrollLeft, wxID_ANY,
|
||||
wxString::Format("Left Item %d", i+1)), 0, wxALL, 5);
|
||||
}
|
||||
m_scrollLeft->SetSizer(leftSizer);
|
||||
|
||||
// Right scrolled window
|
||||
m_scrollRight = new wxScrolledWindow(m_splitter, wxID_ANY);
|
||||
m_scrollRight->SetBackgroundColour(*wxWHITE);
|
||||
m_scrollRight->SetScrollbars(10, 10, 100, 100);
|
||||
|
||||
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
rightSizer->Add(new wxStaticText(m_scrollRight, wxID_ANY,
|
||||
wxString::Format("Right Item %d", i+1)), 0, wxALL, 5);
|
||||
}
|
||||
m_scrollRight->SetSizer(rightSizer);
|
||||
|
||||
m_splitter->SplitVertically(m_scrollLeft, m_scrollRight, 300);
|
||||
m_splitter->SetMinimumPaneSize(100);
|
||||
|
||||
mainSizer->Add(m_splitter, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Drag splitter sash or scroll the panes");
|
||||
|
||||
LogEvent("Layout test app started");
|
||||
LogEvent("Splitter position: 300");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LAYOUT_TEST] wxSplitter/wxScrolled test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void LayoutTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LAYOUT_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void LayoutTestFrame::OnSplitterSashPosChanged(wxSplitterEvent& evt)
|
||||
{
|
||||
LogEvent(wxString::Format("Splitter sash moved to: %d", evt.GetSashPosition()));
|
||||
}
|
||||
|
||||
void LayoutTestFrame::OnScrollWin(wxScrollWinEvent& evt)
|
||||
{
|
||||
LogEvent("Scroll event");
|
||||
evt.Skip();
|
||||
}
|
||||
335
tests/apps/standalone/listctrl/listctrl_test.cpp
Normal file
335
tests/apps/standalone/listctrl/listctrl_test.cpp
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
// wxListCtrl Virtual Mode Test - Tests virtual list control in WASM
|
||||
// KiCad uses virtual mode for large component lists (10000+ items)
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/notebook.h"
|
||||
#include "wx/listctrl.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
// Virtual list control class
|
||||
class VirtualListCtrl : public wxListCtrl
|
||||
{
|
||||
public:
|
||||
VirtualListCtrl(wxWindow* parent, wxWindowID id, int numItems = 10000);
|
||||
|
||||
virtual wxString OnGetItemText(long item, long column) const override;
|
||||
virtual int OnGetItemImage(long item) const override;
|
||||
virtual wxListItemAttr* OnGetItemAttr(long item) const override;
|
||||
|
||||
void SetItemCount(int count);
|
||||
int GetTotalItems() const { return m_numItems; }
|
||||
|
||||
private:
|
||||
int m_numItems;
|
||||
mutable wxListItemAttr m_attr;
|
||||
};
|
||||
|
||||
VirtualListCtrl::VirtualListCtrl(wxWindow* parent, wxWindowID id, int numItems)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
|
||||
wxLC_REPORT | wxLC_VIRTUAL | wxLC_SINGLE_SEL)
|
||||
, m_numItems(numItems)
|
||||
{
|
||||
// Set up columns like KiCad component list
|
||||
InsertColumn(0, "Reference", wxLIST_FORMAT_LEFT, 100);
|
||||
InsertColumn(1, "Value", wxLIST_FORMAT_LEFT, 120);
|
||||
InsertColumn(2, "Footprint", wxLIST_FORMAT_LEFT, 180);
|
||||
InsertColumn(3, "Qty", wxLIST_FORMAT_CENTER, 50);
|
||||
|
||||
SetItemCount(m_numItems);
|
||||
}
|
||||
|
||||
wxString VirtualListCtrl::OnGetItemText(long item, long column) const
|
||||
{
|
||||
switch (column) {
|
||||
case 0: // Reference
|
||||
if (item % 4 == 0) return wxString::Format("R%ld", item + 1);
|
||||
if (item % 4 == 1) return wxString::Format("C%ld", item + 1);
|
||||
if (item % 4 == 2) return wxString::Format("U%ld", item + 1);
|
||||
return wxString::Format("J%ld", item + 1);
|
||||
case 1: // Value
|
||||
if (item % 4 == 0) return wxString::Format("%dk", (item % 10) + 1);
|
||||
if (item % 4 == 1) return wxString::Format("%dnF", (item % 10 + 1) * 10);
|
||||
if (item % 4 == 2) return "STM32F103";
|
||||
return "USB-C";
|
||||
case 2: // Footprint
|
||||
if (item % 4 == 0) return "Resistor_SMD:R_0402";
|
||||
if (item % 4 == 1) return "Capacitor_SMD:C_0402";
|
||||
if (item % 4 == 2) return "Package_QFP:LQFP-48";
|
||||
return "Connector_USB:USB_C";
|
||||
case 3: // Qty
|
||||
return wxString::Format("%ld", (item % 5) + 1);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
int VirtualListCtrl::OnGetItemImage(long WXUNUSED(item)) const
|
||||
{
|
||||
return -1; // No images
|
||||
}
|
||||
|
||||
wxListItemAttr* VirtualListCtrl::OnGetItemAttr(long item) const
|
||||
{
|
||||
// Alternate row colors like KiCad
|
||||
if (item % 2 == 0) {
|
||||
m_attr.SetBackgroundColour(wxColour(245, 245, 245));
|
||||
} else {
|
||||
m_attr.SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
return &m_attr;
|
||||
}
|
||||
|
||||
void VirtualListCtrl::SetItemCount(int count)
|
||||
{
|
||||
m_numItems = count;
|
||||
wxListCtrl::SetItemCount(count);
|
||||
}
|
||||
|
||||
// Main application
|
||||
class ListCtrlTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class ListCtrlTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
ListCtrlTestFrame();
|
||||
|
||||
private:
|
||||
VirtualListCtrl* m_virtualList;
|
||||
wxListCtrl* m_normalList;
|
||||
wxTextCtrl* m_log;
|
||||
wxNotebook* m_notebook;
|
||||
wxStaticText* m_itemCountLabel;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void PopulateNormalList();
|
||||
|
||||
void OnItemSelected(wxListEvent& evt);
|
||||
void OnItemActivated(wxListEvent& evt);
|
||||
void OnColumnClick(wxListEvent& evt);
|
||||
void OnSetItemCount(wxCommandEvent& evt);
|
||||
void OnScrollToItem(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_VIRTUAL_LIST = wxID_HIGHEST + 1,
|
||||
ID_NORMAL_LIST,
|
||||
ID_SET_COUNT_100,
|
||||
ID_SET_COUNT_1000,
|
||||
ID_SET_COUNT_10000,
|
||||
ID_SET_COUNT_100000,
|
||||
ID_SCROLL_TOP,
|
||||
ID_SCROLL_MIDDLE,
|
||||
ID_SCROLL_BOTTOM
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(ListCtrlTestFrame, wxFrame)
|
||||
EVT_LIST_ITEM_SELECTED(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnItemSelected)
|
||||
EVT_LIST_ITEM_ACTIVATED(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnItemActivated)
|
||||
EVT_LIST_COL_CLICK(ID_VIRTUAL_LIST, ListCtrlTestFrame::OnColumnClick)
|
||||
EVT_LIST_ITEM_SELECTED(ID_NORMAL_LIST, ListCtrlTestFrame::OnItemSelected)
|
||||
EVT_BUTTON(ID_SET_COUNT_100, ListCtrlTestFrame::OnSetItemCount)
|
||||
EVT_BUTTON(ID_SET_COUNT_1000, ListCtrlTestFrame::OnSetItemCount)
|
||||
EVT_BUTTON(ID_SET_COUNT_10000, ListCtrlTestFrame::OnSetItemCount)
|
||||
EVT_BUTTON(ID_SET_COUNT_100000, ListCtrlTestFrame::OnSetItemCount)
|
||||
EVT_BUTTON(ID_SCROLL_TOP, ListCtrlTestFrame::OnScrollToItem)
|
||||
EVT_BUTTON(ID_SCROLL_MIDDLE, ListCtrlTestFrame::OnScrollToItem)
|
||||
EVT_BUTTON(ID_SCROLL_BOTTOM, ListCtrlTestFrame::OnScrollToItem)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(ListCtrlTestApp);
|
||||
|
||||
bool ListCtrlTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
ListCtrlTestFrame* frame = new ListCtrlTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
ListCtrlTestFrame::ListCtrlTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxListCtrl Virtual Mode WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 700))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxListCtrl Virtual Mode Test\n\n"
|
||||
"KiCad uses virtual mode wxListCtrl for large component lists.\n"
|
||||
"Virtual mode only creates items on-demand for visible rows.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Controls bar
|
||||
wxStaticBoxSizer* ctrlBox = new wxStaticBoxSizer(wxHORIZONTAL, this, "Virtual List Controls");
|
||||
|
||||
ctrlBox->Add(new wxStaticText(this, wxID_ANY, "Item Count:"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_100, "100"), 0, wxALL, 2);
|
||||
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_1000, "1,000"), 0, wxALL, 2);
|
||||
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_10000, "10,000"), 0, wxALL, 2);
|
||||
ctrlBox->Add(new wxButton(this, ID_SET_COUNT_100000, "100,000"), 0, wxALL, 2);
|
||||
|
||||
ctrlBox->AddSpacer(20);
|
||||
|
||||
ctrlBox->Add(new wxStaticText(this, wxID_ANY, "Scroll:"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
ctrlBox->Add(new wxButton(this, ID_SCROLL_TOP, "Top"), 0, wxALL, 2);
|
||||
ctrlBox->Add(new wxButton(this, ID_SCROLL_MIDDLE, "Middle"), 0, wxALL, 2);
|
||||
ctrlBox->Add(new wxButton(this, ID_SCROLL_BOTTOM, "Bottom"), 0, wxALL, 2);
|
||||
|
||||
mainSizer->Add(ctrlBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Item count display
|
||||
m_itemCountLabel = new wxStaticText(this, wxID_ANY, "Current items: 10,000");
|
||||
mainSizer->Add(m_itemCountLabel, 0, wxLEFT, 15);
|
||||
|
||||
// Notebook with two tabs
|
||||
m_notebook = new wxNotebook(this, wxID_ANY);
|
||||
|
||||
// Tab 1: Virtual List
|
||||
wxPanel* virtualPanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* virtualSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_virtualList = new VirtualListCtrl(virtualPanel, ID_VIRTUAL_LIST, 10000);
|
||||
virtualSizer->Add(m_virtualList, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
virtualPanel->SetSizer(virtualSizer);
|
||||
m_notebook->AddPage(virtualPanel, "Virtual List (10,000 items)");
|
||||
|
||||
// Tab 2: Normal List (for comparison)
|
||||
wxPanel* normalPanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* normalSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_normalList = new wxListCtrl(normalPanel, ID_NORMAL_LIST,
|
||||
wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
|
||||
m_normalList->InsertColumn(0, "Reference", wxLIST_FORMAT_LEFT, 100);
|
||||
m_normalList->InsertColumn(1, "Value", wxLIST_FORMAT_LEFT, 120);
|
||||
normalSizer->Add(m_normalList, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
normalPanel->SetSizer(normalSizer);
|
||||
m_notebook->AddPage(normalPanel, "Normal List (100 items)");
|
||||
|
||||
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - Virtual ListCtrl test");
|
||||
|
||||
PopulateNormalList();
|
||||
|
||||
LogEvent("ListCtrl test app started");
|
||||
LogEvent("Virtual list: 10,000 items (only visible rows created)");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LISTCTRL_TEST] wxListCtrl virtual mode test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::PopulateNormalList()
|
||||
{
|
||||
for (int i = 0; i < 100; i++) {
|
||||
long idx = m_normalList->InsertItem(i, wxString::Format("Item %d", i + 1));
|
||||
m_normalList->SetItem(idx, 1, wxString::Format("Value %d", i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LISTCTRL_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::OnItemSelected(wxListEvent& evt)
|
||||
{
|
||||
wxString listName = (evt.GetId() == ID_VIRTUAL_LIST) ? "Virtual" : "Normal";
|
||||
LogEvent(wxString::Format("%s list: Selected item %ld", listName, evt.GetIndex()));
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::OnItemActivated(wxListEvent& evt)
|
||||
{
|
||||
LogEvent(wxString::Format("Virtual list: Activated item %ld (double-click)", evt.GetIndex()));
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::OnColumnClick(wxListEvent& evt)
|
||||
{
|
||||
LogEvent(wxString::Format("Column %d clicked (would sort)", evt.GetColumn()));
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::OnSetItemCount(wxCommandEvent& evt)
|
||||
{
|
||||
int count = 0;
|
||||
switch (evt.GetId()) {
|
||||
case ID_SET_COUNT_100: count = 100; break;
|
||||
case ID_SET_COUNT_1000: count = 1000; break;
|
||||
case ID_SET_COUNT_10000: count = 10000; break;
|
||||
case ID_SET_COUNT_100000: count = 100000; break;
|
||||
}
|
||||
|
||||
m_virtualList->SetItemCount(count);
|
||||
|
||||
wxString countStr;
|
||||
if (count >= 1000) {
|
||||
countStr = wxString::Format("%d,%03d", count / 1000, count % 1000);
|
||||
} else {
|
||||
countStr = wxString::Format("%d", count);
|
||||
}
|
||||
m_itemCountLabel->SetLabel(wxString::Format("Current items: %s", countStr));
|
||||
|
||||
// Update tab name
|
||||
m_notebook->SetPageText(0, wxString::Format("Virtual List (%s items)", countStr));
|
||||
|
||||
LogEvent(wxString::Format("Set virtual list to %s items", countStr));
|
||||
}
|
||||
|
||||
void ListCtrlTestFrame::OnScrollToItem(wxCommandEvent& evt)
|
||||
{
|
||||
int totalItems = m_virtualList->GetTotalItems();
|
||||
long targetItem = 0;
|
||||
|
||||
switch (evt.GetId()) {
|
||||
case ID_SCROLL_TOP:
|
||||
targetItem = 0;
|
||||
break;
|
||||
case ID_SCROLL_MIDDLE:
|
||||
targetItem = totalItems / 2;
|
||||
break;
|
||||
case ID_SCROLL_BOTTOM:
|
||||
targetItem = totalItems - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
m_virtualList->EnsureVisible(targetItem);
|
||||
m_virtualList->SetItemState(targetItem, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
|
||||
LogEvent(wxString::Format("Scrolled to item %ld", targetItem));
|
||||
}
|
||||
215
tests/apps/standalone/logerror/logerror_test.cpp
Normal file
215
tests/apps/standalone/logerror/logerror_test.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// wxLogError Dialog Test - Reproduces KiCad's kiface error dialog
|
||||
// Tests wxLogDialog with wxCollapsiblePane "Details" dropdown
|
||||
// Used to debug dialog positioning and wxLog console logging
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/log.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class LogErrorTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class LogErrorTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
LogErrorTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
// Single error - like KiCad's "Error loading editor."
|
||||
void OnSingleError(wxCommandEvent& evt);
|
||||
|
||||
// Multiple errors - triggers Details dropdown with wxListCtrl
|
||||
void OnMultipleErrors(wxCommandEvent& evt);
|
||||
|
||||
// Mix of error levels
|
||||
void OnMixedLevels(wxCommandEvent& evt);
|
||||
|
||||
// Manually flush the log to show dialog
|
||||
void OnFlushLog(wxCommandEvent& evt);
|
||||
|
||||
// Clear logged messages without showing dialog
|
||||
void OnClearLog(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_SINGLE_ERROR = wxID_HIGHEST + 1,
|
||||
ID_MULTIPLE_ERRORS,
|
||||
ID_MIXED_LEVELS,
|
||||
ID_FLUSH_LOG,
|
||||
ID_CLEAR_LOG
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(LogErrorTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_SINGLE_ERROR, LogErrorTestFrame::OnSingleError)
|
||||
EVT_BUTTON(ID_MULTIPLE_ERRORS, LogErrorTestFrame::OnMultipleErrors)
|
||||
EVT_BUTTON(ID_MIXED_LEVELS, LogErrorTestFrame::OnMixedLevels)
|
||||
EVT_BUTTON(ID_FLUSH_LOG, LogErrorTestFrame::OnFlushLog)
|
||||
EVT_BUTTON(ID_CLEAR_LOG, LogErrorTestFrame::OnClearLog)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(LogErrorTestApp);
|
||||
|
||||
bool LogErrorTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
LogErrorTestFrame* frame = new LogErrorTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
LogErrorTestFrame::LogErrorTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxLogError Dialog Test",
|
||||
wxDefaultPosition, wxSize(700, 550))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxLogError Dialog Test\n\n"
|
||||
"This test reproduces the exact dialog KiCad shows when kiface loading fails.\n"
|
||||
"It uses wxLogDialog with wxCollapsiblePane 'Details' dropdown.\n"
|
||||
"Watch the browser console for [wxLog] messages.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Single error section - like KiCad's error
|
||||
wxStaticBoxSizer* singleBox = new wxStaticBoxSizer(wxVERTICAL, this, "Single Error (KiCad-style)");
|
||||
wxBoxSizer* singleBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
singleBtnSizer->Add(new wxButton(this, ID_SINGLE_ERROR, "Trigger Error"), 0, wxALL, 5);
|
||||
singleBtnSizer->Add(new wxStaticText(this, wxID_ANY, "Calls wxLogError(\"Error loading editor.\")"),
|
||||
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
singleBox->Add(singleBtnSizer, 0, wxALIGN_LEFT);
|
||||
mainSizer->Add(singleBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Multiple errors section - triggers Details dropdown
|
||||
wxStaticBoxSizer* multiBox = new wxStaticBoxSizer(wxVERTICAL, this, "Multiple Errors (Details dropdown)");
|
||||
wxBoxSizer* multiBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
multiBtnSizer->Add(new wxButton(this, ID_MULTIPLE_ERRORS, "Trigger Multiple"), 0, wxALL, 5);
|
||||
multiBtnSizer->Add(new wxStaticText(this, wxID_ANY, "Logs 3 errors - shows Details with wxListCtrl"),
|
||||
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
multiBox->Add(multiBtnSizer, 0, wxALIGN_LEFT);
|
||||
mainSizer->Add(multiBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Mixed levels section
|
||||
wxStaticBoxSizer* mixedBox = new wxStaticBoxSizer(wxVERTICAL, this, "Mixed Log Levels");
|
||||
wxBoxSizer* mixedBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
mixedBtnSizer->Add(new wxButton(this, ID_MIXED_LEVELS, "Mixed Levels"), 0, wxALL, 5);
|
||||
mixedBtnSizer->Add(new wxStaticText(this, wxID_ANY, "wxLogError + wxLogWarning + wxLogMessage"),
|
||||
0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
mixedBox->Add(mixedBtnSizer, 0, wxALIGN_LEFT);
|
||||
mainSizer->Add(mixedBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Control buttons
|
||||
wxStaticBoxSizer* controlBox = new wxStaticBoxSizer(wxHORIZONTAL, this, "Log Control");
|
||||
controlBox->Add(new wxButton(this, ID_FLUSH_LOG, "Flush Log (Show Dialog)"), 0, wxALL, 5);
|
||||
controlBox->Add(new wxButton(this, ID_CLEAR_LOG, "Clear Log"), 0, wxALL, 5);
|
||||
mainSizer->Add(controlBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - Click buttons to trigger wxLog errors");
|
||||
|
||||
LogEvent("wxLogError test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LOGERROR_TEST] wxLogError test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[LOGERROR_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::OnSingleError(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Triggering single wxLogError...");
|
||||
|
||||
// This is exactly what KiCad does in kiway.cpp
|
||||
wxLogError("Error loading editor.");
|
||||
|
||||
LogEvent("wxLogError called - dialog should appear on next event loop or Flush");
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::OnMultipleErrors(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Triggering multiple wxLogError calls...");
|
||||
|
||||
// Multiple errors trigger the Details dropdown with wxListCtrl
|
||||
wxLogError("Failed to load shared library '/usr/bin/_pcbnew.kiface'");
|
||||
wxLogError("IO_ERROR: Failed to load kiface library");
|
||||
wxLogError("Error loading editor.");
|
||||
|
||||
LogEvent("3 errors logged - Details dropdown should appear");
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::OnMixedLevels(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Triggering mixed log levels...");
|
||||
|
||||
wxLogError("This is an error message");
|
||||
wxLogWarning("This is a warning message");
|
||||
wxLogMessage("This is an info message");
|
||||
|
||||
LogEvent("Mixed levels logged - check console for [wxLog] output");
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::OnFlushLog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Flushing log - dialog should appear now...");
|
||||
|
||||
// Force the log to flush, which shows the dialog
|
||||
wxLog::FlushActive();
|
||||
|
||||
LogEvent("Flush complete - dialog should have been shown");
|
||||
}
|
||||
|
||||
void LogErrorTestFrame::OnClearLog(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Clearing accumulated log messages...");
|
||||
|
||||
// Get the active log and clear it without showing dialog
|
||||
wxLog* log = wxLog::GetActiveTarget();
|
||||
if (log)
|
||||
{
|
||||
// Disable logging temporarily to clear without showing
|
||||
wxLogNull noLog;
|
||||
// The accumulated messages will be discarded
|
||||
}
|
||||
|
||||
LogEvent("Log cleared");
|
||||
}
|
||||
179
tests/apps/standalone/maximize/maximize_test.cpp
Normal file
179
tests/apps/standalone/maximize/maximize_test.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// Maximize Test - Tests wxFrame::Maximize() which KiCad uses
|
||||
// This reproduces the bug where Maximize() returns a tiny window when
|
||||
// GetScreenSize() returns incorrect values at startup.
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/display.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class MaximizeTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class MaximizeTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
MaximizeTestFrame();
|
||||
|
||||
private:
|
||||
wxStaticText* m_sizeLabel;
|
||||
wxStaticText* m_displayLabel;
|
||||
|
||||
void UpdateSizeDisplay();
|
||||
void OnSize(wxSizeEvent& evt);
|
||||
void OnMaximize(wxCommandEvent& evt);
|
||||
void OnRestore(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_MAXIMIZE = wxID_HIGHEST + 1,
|
||||
ID_RESTORE
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(MaximizeTestFrame, wxFrame)
|
||||
EVT_SIZE(MaximizeTestFrame::OnSize)
|
||||
EVT_BUTTON(ID_MAXIMIZE, MaximizeTestFrame::OnMaximize)
|
||||
EVT_BUTTON(ID_RESTORE, MaximizeTestFrame::OnRestore)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(MaximizeTestApp);
|
||||
|
||||
bool MaximizeTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
// Create frame WITHOUT explicit size - uses wxDefaultSize
|
||||
// This is what KiCad does
|
||||
MaximizeTestFrame* frame = new MaximizeTestFrame();
|
||||
|
||||
// KiCad calls Maximize() after creation
|
||||
frame->Maximize(true);
|
||||
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
MaximizeTestFrame::MaximizeTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "Maximize Test",
|
||||
wxDefaultPosition, wxDefaultSize) // No explicit size!
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"Maximize Test\n\n"
|
||||
"This test reproduces the KiCad startup issue.\n"
|
||||
"The frame is created with wxDefaultSize and Maximize() is called.\n"
|
||||
"If display size detection works, the window should be fullscreen.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Display info
|
||||
wxStaticBoxSizer* infoBox = new wxStaticBoxSizer(wxVERTICAL, this, "Display Info");
|
||||
|
||||
m_displayLabel = new wxStaticText(this, wxID_ANY, "Display: querying...");
|
||||
infoBox->Add(m_displayLabel, 0, wxALL, 5);
|
||||
|
||||
m_sizeLabel = new wxStaticText(this, wxID_ANY, "Window size: querying...");
|
||||
infoBox->Add(m_sizeLabel, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(infoBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_MAXIMIZE, "Maximize"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_RESTORE, "Restore"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
// Query display info
|
||||
wxDisplay display(wxDisplay::GetFromWindow(this));
|
||||
wxRect geom = display.GetGeometry();
|
||||
wxRect client = display.GetClientArea();
|
||||
|
||||
wxString displayInfo = wxString::Format(
|
||||
"Display geometry: %dx%d, Client area: %dx%d",
|
||||
geom.GetWidth(), geom.GetHeight(),
|
||||
client.GetWidth(), client.GetHeight());
|
||||
m_displayLabel->SetLabel(displayInfo);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[MAXIMIZE_TEST] Display geometry: ' + $0 + 'x' + $1);
|
||||
console.log('[MAXIMIZE_TEST] Client area: ' + $2 + 'x' + $3);
|
||||
}, geom.GetWidth(), geom.GetHeight(), client.GetWidth(), client.GetHeight());
|
||||
#endif
|
||||
|
||||
// Update size display after creation
|
||||
CallAfter(&MaximizeTestFrame::UpdateSizeDisplay);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[MAXIMIZE_TEST] Maximize test app started');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaximizeTestFrame::UpdateSizeDisplay()
|
||||
{
|
||||
wxSize size = GetSize();
|
||||
wxSize clientSize = GetClientSize();
|
||||
|
||||
wxString sizeInfo = wxString::Format(
|
||||
"Window size: %dx%d, Client: %dx%d, Maximized: %s",
|
||||
size.GetWidth(), size.GetHeight(),
|
||||
clientSize.GetWidth(), clientSize.GetHeight(),
|
||||
IsMaximized() ? "YES" : "NO");
|
||||
m_sizeLabel->SetLabel(sizeInfo);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[MAXIMIZE_TEST] Window size: ' + $0 + 'x' + $1);
|
||||
console.log('[MAXIMIZE_TEST] Client size: ' + $2 + 'x' + $3);
|
||||
console.log('[MAXIMIZE_TEST] Is maximized: ' + ($4 ? 'YES' : 'NO'));
|
||||
|
||||
// Test assertion: window should be larger than 100px if maximize worked
|
||||
if ($0 < 100 || $1 < 100) {
|
||||
console.error('[MAXIMIZE_TEST] FAIL: Window is too small! Expected fullscreen, got ' + $0 + 'x' + $1);
|
||||
} else {
|
||||
console.log('[MAXIMIZE_TEST] PASS: Window size is reasonable');
|
||||
}
|
||||
}, size.GetWidth(), size.GetHeight(),
|
||||
clientSize.GetWidth(), clientSize.GetHeight(),
|
||||
IsMaximized() ? 1 : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaximizeTestFrame::OnSize(wxSizeEvent& evt)
|
||||
{
|
||||
evt.Skip();
|
||||
CallAfter(&MaximizeTestFrame::UpdateSizeDisplay);
|
||||
}
|
||||
|
||||
void MaximizeTestFrame::OnMaximize(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[MAXIMIZE_TEST] Maximize button clicked'); });
|
||||
#endif
|
||||
Maximize(true);
|
||||
}
|
||||
|
||||
void MaximizeTestFrame::OnRestore(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({ console.log('[MAXIMIZE_TEST] Restore button clicked'); });
|
||||
#endif
|
||||
Maximize(false);
|
||||
}
|
||||
300
tests/apps/standalone/menu/menu_test.cpp
Normal file
300
tests/apps/standalone/menu/menu_test.cpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// wxMenuBar Test - Tests menu functionality in WASM
|
||||
// KiCad uses extensive menus for File, Edit, View, Place, Route, etc.
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class MenuTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class MenuTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
MenuTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
// Menu event handlers
|
||||
void OnMenuNew(wxCommandEvent& evt);
|
||||
void OnMenuOpen(wxCommandEvent& evt);
|
||||
void OnMenuSave(wxCommandEvent& evt);
|
||||
void OnMenuSaveAs(wxCommandEvent& evt);
|
||||
void OnMenuExit(wxCommandEvent& evt);
|
||||
|
||||
void OnMenuUndo(wxCommandEvent& evt);
|
||||
void OnMenuRedo(wxCommandEvent& evt);
|
||||
void OnMenuCut(wxCommandEvent& evt);
|
||||
void OnMenuCopy(wxCommandEvent& evt);
|
||||
void OnMenuPaste(wxCommandEvent& evt);
|
||||
void OnMenuSelectAll(wxCommandEvent& evt);
|
||||
|
||||
void OnMenuZoomIn(wxCommandEvent& evt);
|
||||
void OnMenuZoomOut(wxCommandEvent& evt);
|
||||
void OnMenuZoomFit(wxCommandEvent& evt);
|
||||
void OnMenuFullScreen(wxCommandEvent& evt);
|
||||
|
||||
void OnMenuPreferences(wxCommandEvent& evt);
|
||||
void OnMenuAbout(wxCommandEvent& evt);
|
||||
void OnMenuHelp(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_NEW = wxID_HIGHEST + 1,
|
||||
ID_OPEN,
|
||||
ID_SAVE,
|
||||
ID_SAVE_AS,
|
||||
ID_UNDO,
|
||||
ID_REDO,
|
||||
ID_CUT,
|
||||
ID_COPY,
|
||||
ID_PASTE,
|
||||
ID_SELECT_ALL,
|
||||
ID_ZOOM_IN,
|
||||
ID_ZOOM_OUT,
|
||||
ID_ZOOM_FIT,
|
||||
ID_FULLSCREEN,
|
||||
ID_PREFERENCES,
|
||||
ID_HELP_CONTENTS
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(MenuTestFrame, wxFrame)
|
||||
EVT_MENU(ID_NEW, MenuTestFrame::OnMenuNew)
|
||||
EVT_MENU(ID_OPEN, MenuTestFrame::OnMenuOpen)
|
||||
EVT_MENU(ID_SAVE, MenuTestFrame::OnMenuSave)
|
||||
EVT_MENU(ID_SAVE_AS, MenuTestFrame::OnMenuSaveAs)
|
||||
EVT_MENU(wxID_EXIT, MenuTestFrame::OnMenuExit)
|
||||
EVT_MENU(ID_UNDO, MenuTestFrame::OnMenuUndo)
|
||||
EVT_MENU(ID_REDO, MenuTestFrame::OnMenuRedo)
|
||||
EVT_MENU(ID_CUT, MenuTestFrame::OnMenuCut)
|
||||
EVT_MENU(ID_COPY, MenuTestFrame::OnMenuCopy)
|
||||
EVT_MENU(ID_PASTE, MenuTestFrame::OnMenuPaste)
|
||||
EVT_MENU(ID_SELECT_ALL, MenuTestFrame::OnMenuSelectAll)
|
||||
EVT_MENU(ID_ZOOM_IN, MenuTestFrame::OnMenuZoomIn)
|
||||
EVT_MENU(ID_ZOOM_OUT, MenuTestFrame::OnMenuZoomOut)
|
||||
EVT_MENU(ID_ZOOM_FIT, MenuTestFrame::OnMenuZoomFit)
|
||||
EVT_MENU(ID_FULLSCREEN, MenuTestFrame::OnMenuFullScreen)
|
||||
EVT_MENU(ID_PREFERENCES, MenuTestFrame::OnMenuPreferences)
|
||||
EVT_MENU(wxID_ABOUT, MenuTestFrame::OnMenuAbout)
|
||||
EVT_MENU(ID_HELP_CONTENTS, MenuTestFrame::OnMenuHelp)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(MenuTestApp);
|
||||
|
||||
bool MenuTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
MenuTestFrame* frame = new MenuTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuTestFrame::MenuTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxMenuBar WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 400))
|
||||
{
|
||||
// Create File menu
|
||||
wxMenu* menuFile = new wxMenu;
|
||||
menuFile->Append(ID_NEW, "&New\tCtrl+N", "Create a new file");
|
||||
menuFile->Append(ID_OPEN, "&Open...\tCtrl+O", "Open an existing file");
|
||||
menuFile->AppendSeparator();
|
||||
menuFile->Append(ID_SAVE, "&Save\tCtrl+S", "Save the current file");
|
||||
menuFile->Append(ID_SAVE_AS, "Save &As...\tCtrl+Shift+S", "Save with a new name");
|
||||
menuFile->AppendSeparator();
|
||||
menuFile->Append(wxID_EXIT, "E&xit\tAlt+F4", "Exit the application");
|
||||
|
||||
// Create Edit menu
|
||||
wxMenu* menuEdit = new wxMenu;
|
||||
menuEdit->Append(ID_UNDO, "&Undo\tCtrl+Z", "Undo the last action");
|
||||
menuEdit->Append(ID_REDO, "&Redo\tCtrl+Y", "Redo the last undone action");
|
||||
menuEdit->AppendSeparator();
|
||||
menuEdit->Append(ID_CUT, "Cu&t\tCtrl+X", "Cut selection to clipboard");
|
||||
menuEdit->Append(ID_COPY, "&Copy\tCtrl+C", "Copy selection to clipboard");
|
||||
menuEdit->Append(ID_PASTE, "&Paste\tCtrl+V", "Paste from clipboard");
|
||||
menuEdit->AppendSeparator();
|
||||
menuEdit->Append(ID_SELECT_ALL, "Select &All\tCtrl+A", "Select all");
|
||||
|
||||
// Create View menu
|
||||
wxMenu* menuView = new wxMenu;
|
||||
menuView->Append(ID_ZOOM_IN, "Zoom &In\tCtrl++", "Zoom in");
|
||||
menuView->Append(ID_ZOOM_OUT, "Zoom &Out\tCtrl+-", "Zoom out");
|
||||
menuView->Append(ID_ZOOM_FIT, "Zoom to &Fit\tCtrl+0", "Fit view to window");
|
||||
menuView->AppendSeparator();
|
||||
menuView->AppendCheckItem(ID_FULLSCREEN, "&Full Screen\tF11", "Toggle full screen mode");
|
||||
|
||||
// Create Tools menu (like KiCad's preferences)
|
||||
wxMenu* menuTools = new wxMenu;
|
||||
menuTools->Append(ID_PREFERENCES, "&Preferences...", "Open preferences dialog");
|
||||
|
||||
// Create Help menu
|
||||
wxMenu* menuHelp = new wxMenu;
|
||||
menuHelp->Append(ID_HELP_CONTENTS, "&Help Contents\tF1", "Show help");
|
||||
menuHelp->AppendSeparator();
|
||||
menuHelp->Append(wxID_ABOUT, "&About...", "About this application");
|
||||
|
||||
// Create menu bar
|
||||
wxMenuBar* menuBar = new wxMenuBar;
|
||||
menuBar->Append(menuFile, "&File");
|
||||
menuBar->Append(menuEdit, "&Edit");
|
||||
menuBar->Append(menuView, "&View");
|
||||
menuBar->Append(menuTools, "&Tools");
|
||||
menuBar->Append(menuHelp, "&Help");
|
||||
|
||||
SetMenuBar(menuBar);
|
||||
|
||||
// Create main content - NO GL canvas
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxMenuBar Test\n\n"
|
||||
"This tests the menu system which KiCad uses extensively.\n"
|
||||
"Click menu items to see events logged below.");
|
||||
sizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 200),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(m_log, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(sizer);
|
||||
|
||||
// Create status bar
|
||||
CreateStatusBar(2);
|
||||
SetStatusText("Ready");
|
||||
SetStatusText("Menu test", 1);
|
||||
|
||||
LogEvent("Menu test app started");
|
||||
LogEvent("Menu bar created with File, Edit, View, Tools, Help menus");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[MENU_TEST] wxMenuBar test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void MenuTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[MENU_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
// File menu handlers
|
||||
void MenuTestFrame::OnMenuNew(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("File > New clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuOpen(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("File > Open clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuSave(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("File > Save clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuSaveAs(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("File > Save As clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuExit(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("File > Exit clicked");
|
||||
Close(true);
|
||||
}
|
||||
|
||||
// Edit menu handlers
|
||||
void MenuTestFrame::OnMenuUndo(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Undo clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuRedo(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Redo clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuCut(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Cut clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuCopy(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Copy clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuPaste(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Paste clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuSelectAll(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Edit > Select All clicked");
|
||||
}
|
||||
|
||||
// View menu handlers
|
||||
void MenuTestFrame::OnMenuZoomIn(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("View > Zoom In clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuZoomOut(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("View > Zoom Out clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuZoomFit(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("View > Zoom to Fit clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuFullScreen(wxCommandEvent& evt)
|
||||
{
|
||||
bool isFullScreen = evt.IsChecked();
|
||||
LogEvent(wxString::Format("View > Full Screen toggled: %s",
|
||||
isFullScreen ? "ON" : "OFF"));
|
||||
ShowFullScreen(isFullScreen);
|
||||
}
|
||||
|
||||
// Tools menu handlers
|
||||
void MenuTestFrame::OnMenuPreferences(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Tools > Preferences clicked");
|
||||
}
|
||||
|
||||
// Help menu handlers
|
||||
void MenuTestFrame::OnMenuAbout(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Help > About clicked");
|
||||
}
|
||||
|
||||
void MenuTestFrame::OnMenuHelp(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Help > Help Contents clicked");
|
||||
}
|
||||
330
tests/apps/standalone/ownerdrawn/ownerdrawn_test.cpp
Normal file
330
tests/apps/standalone/ownerdrawn/ownerdrawn_test.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
// wxOwnerDrawnComboBox Test - Custom dropdown rendering like KiCad's layer/font selectors
|
||||
// Tests: wxOwnerDrawnComboBox, OnDrawItem, OnMeasureItem, custom rendering
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/odcombo.h"
|
||||
#include "wx/dcmemory.h"
|
||||
|
||||
// Custom owner-drawn combo box for layer selection (like KiCad LAYER_BOX_SELECTOR)
|
||||
class LayerComboBox : public wxOwnerDrawnComboBox
|
||||
{
|
||||
public:
|
||||
LayerComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
|
||||
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
|
||||
wxSize(200, -1), 0, nullptr, wxCB_READONLY)
|
||||
{
|
||||
// Add layers with colors
|
||||
Append("F.Cu"); m_colors.push_back(*wxRED);
|
||||
Append("B.Cu"); m_colors.push_back(*wxBLUE);
|
||||
Append("F.SilkS"); m_colors.push_back(*wxYELLOW);
|
||||
Append("B.SilkS"); m_colors.push_back(wxColour(255, 0, 255));
|
||||
Append("F.Mask"); m_colors.push_back(wxColour(0, 128, 0));
|
||||
Append("B.Mask"); m_colors.push_back(wxColour(0, 128, 128));
|
||||
Append("Edge.Cuts"); m_colors.push_back(*wxWHITE);
|
||||
Append("Dwgs.User"); m_colors.push_back(wxColour(128, 128, 128));
|
||||
|
||||
SetSelection(0);
|
||||
}
|
||||
|
||||
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
|
||||
{
|
||||
if (item == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
// Draw background
|
||||
if (flags & wxODCB_PAINTING_SELECTED)
|
||||
{
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect);
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
}
|
||||
|
||||
// Draw color swatch
|
||||
wxRect swatchRect(rect.x + 4, rect.y + 4, 20, rect.height - 8);
|
||||
dc.SetBrush(wxBrush(m_colors[item]));
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.DrawRectangle(swatchRect);
|
||||
|
||||
// Draw text
|
||||
wxString text = GetString(item);
|
||||
dc.DrawText(text, rect.x + 30, rect.y + (rect.height - dc.GetCharHeight()) / 2);
|
||||
}
|
||||
|
||||
virtual wxCoord OnMeasureItem(size_t item) const override
|
||||
{
|
||||
return 24; // Fixed item height
|
||||
}
|
||||
|
||||
virtual wxCoord OnMeasureItemWidth(size_t item) const override
|
||||
{
|
||||
return -1; // Use default width
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<wxColour> m_colors;
|
||||
};
|
||||
|
||||
// Custom owner-drawn combo box for fonts (like KiCad FONT_CHOICE)
|
||||
class FontComboBox : public wxOwnerDrawnComboBox
|
||||
{
|
||||
public:
|
||||
FontComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
|
||||
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
|
||||
wxSize(200, -1), 0, nullptr, wxCB_READONLY)
|
||||
{
|
||||
// Add sample fonts
|
||||
Append("Default");
|
||||
Append("Arial");
|
||||
Append("Times New Roman");
|
||||
Append("Courier New");
|
||||
Append("Verdana");
|
||||
Append("Georgia");
|
||||
Append("Comic Sans MS");
|
||||
Append("Impact");
|
||||
|
||||
SetSelection(0);
|
||||
}
|
||||
|
||||
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
|
||||
{
|
||||
if (item == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
// Draw background
|
||||
if (flags & wxODCB_PAINTING_SELECTED)
|
||||
{
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect);
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
}
|
||||
|
||||
// Draw text in the font itself (if not Default)
|
||||
wxString fontName = GetString(item);
|
||||
wxFont font = dc.GetFont();
|
||||
if (fontName != "Default")
|
||||
{
|
||||
font.SetFaceName(fontName);
|
||||
}
|
||||
dc.SetFont(font);
|
||||
dc.DrawText(fontName, rect.x + 6, rect.y + (rect.height - dc.GetCharHeight()) / 2);
|
||||
}
|
||||
|
||||
virtual wxCoord OnMeasureItem(size_t item) const override
|
||||
{
|
||||
return 26; // Slightly taller for fonts
|
||||
}
|
||||
};
|
||||
|
||||
// Custom owner-drawn combo box with icons (for footprints)
|
||||
class IconComboBox : public wxOwnerDrawnComboBox
|
||||
{
|
||||
public:
|
||||
IconComboBox(wxWindow* parent, wxWindowID id = wxID_ANY)
|
||||
: wxOwnerDrawnComboBox(parent, id, wxEmptyString, wxDefaultPosition,
|
||||
wxSize(250, -1), 0, nullptr, wxCB_READONLY)
|
||||
{
|
||||
// Add items with different icon types
|
||||
Append("Resistor"); m_types.push_back(0); // Rectangle
|
||||
Append("Capacitor"); m_types.push_back(1); // Two lines
|
||||
Append("Inductor"); m_types.push_back(2); // Coil
|
||||
Append("Diode"); m_types.push_back(3); // Triangle
|
||||
Append("Transistor"); m_types.push_back(4); // Complex
|
||||
Append("IC Package"); m_types.push_back(5); // Square with pins
|
||||
Append("Connector"); m_types.push_back(6); // Dots
|
||||
|
||||
SetSelection(0);
|
||||
}
|
||||
|
||||
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override
|
||||
{
|
||||
if (item == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
// Draw background
|
||||
if (flags & wxODCB_PAINTING_SELECTED)
|
||||
{
|
||||
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.DrawRectangle(rect);
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
}
|
||||
|
||||
// Draw icon based on type
|
||||
int iconX = rect.x + 4;
|
||||
int iconY = rect.y + 4;
|
||||
int iconSize = rect.height - 8;
|
||||
|
||||
dc.SetPen(wxPen(*wxBLACK, 2));
|
||||
dc.SetBrush(*wxWHITE_BRUSH);
|
||||
|
||||
int type = m_types[item];
|
||||
switch (type)
|
||||
{
|
||||
case 0: // Resistor - zigzag
|
||||
dc.DrawLine(iconX, iconY + iconSize/2, iconX + iconSize, iconY + iconSize/2);
|
||||
break;
|
||||
case 1: // Capacitor - two lines
|
||||
dc.DrawLine(iconX + iconSize/3, iconY + 2, iconX + iconSize/3, iconY + iconSize - 2);
|
||||
dc.DrawLine(iconX + 2*iconSize/3, iconY + 2, iconX + 2*iconSize/3, iconY + iconSize - 2);
|
||||
break;
|
||||
case 2: // Inductor - coil (3 bumps)
|
||||
dc.DrawArc(iconX + 4, iconY + iconSize/2, iconX + 10, iconY + iconSize/2, iconX + 7, iconY + iconSize/2 - 4);
|
||||
break;
|
||||
case 3: // Diode - triangle
|
||||
{
|
||||
wxPoint pts[3] = {
|
||||
wxPoint(iconX + 2, iconY + 2),
|
||||
wxPoint(iconX + 2, iconY + iconSize - 2),
|
||||
wxPoint(iconX + iconSize - 2, iconY + iconSize/2)
|
||||
};
|
||||
dc.DrawPolygon(3, pts);
|
||||
}
|
||||
break;
|
||||
case 4: // Transistor - circle with lines
|
||||
dc.DrawCircle(iconX + iconSize/2, iconY + iconSize/2, iconSize/3);
|
||||
break;
|
||||
case 5: // IC - square
|
||||
dc.DrawRectangle(iconX + 2, iconY + 2, iconSize - 4, iconSize - 4);
|
||||
break;
|
||||
case 6: // Connector - dots
|
||||
dc.SetBrush(*wxBLACK_BRUSH);
|
||||
dc.DrawCircle(iconX + iconSize/4, iconY + iconSize/2, 3);
|
||||
dc.DrawCircle(iconX + 3*iconSize/4, iconY + iconSize/2, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw text
|
||||
wxString text = GetString(item);
|
||||
dc.DrawText(text, rect.x + iconSize + 10, rect.y + (rect.height - dc.GetCharHeight()) / 2);
|
||||
}
|
||||
|
||||
virtual wxCoord OnMeasureItem(size_t item) const override
|
||||
{
|
||||
return 28;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> m_types;
|
||||
};
|
||||
|
||||
class OwnerDrawnFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
OwnerDrawnFrame() : wxFrame(nullptr, wxID_ANY, "wxOwnerDrawnComboBox Test",
|
||||
wxDefaultPosition, wxSize(700, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxOwnerDrawnComboBox for layer selectors and font choosers.\n"
|
||||
"Tests: Custom item drawing, variable heights, icons + text.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Layer selector
|
||||
wxStaticBoxSizer* layerSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Layer Selector (Color Swatches)");
|
||||
m_layerCombo = new LayerComboBox(mainPanel);
|
||||
m_layerCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnLayerChanged, this);
|
||||
layerSizer->Add(m_layerCombo, 0, wxALL, 5);
|
||||
m_layerLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: F.Cu");
|
||||
layerSizer->Add(m_layerLabel, 0, wxALL, 5);
|
||||
mainSizer->Add(layerSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Font selector
|
||||
wxStaticBoxSizer* fontSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Font Selector (Font Preview)");
|
||||
m_fontCombo = new FontComboBox(mainPanel);
|
||||
m_fontCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnFontChanged, this);
|
||||
fontSizer->Add(m_fontCombo, 0, wxALL, 5);
|
||||
m_fontLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: Default");
|
||||
fontSizer->Add(m_fontLabel, 0, wxALL, 5);
|
||||
mainSizer->Add(fontSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Icon selector
|
||||
wxStaticBoxSizer* iconSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Component Selector (Icons)");
|
||||
m_iconCombo = new IconComboBox(mainPanel);
|
||||
m_iconCombo->Bind(wxEVT_COMBOBOX, &OwnerDrawnFrame::OnIconChanged, this);
|
||||
iconSizer->Add(m_iconCombo, 0, wxALL, 5);
|
||||
m_iconLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: Resistor");
|
||||
iconSizer->Add(m_iconLabel, 0, wxALL, 5);
|
||||
mainSizer->Add(iconSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Owner-drawn combo box test app started");
|
||||
Log("Owner-drawn combo box test app started");
|
||||
Log("Each dropdown shows custom-rendered items");
|
||||
}
|
||||
|
||||
private:
|
||||
void OnLayerChanged(wxCommandEvent& event)
|
||||
{
|
||||
int sel = m_layerCombo->GetSelection();
|
||||
wxString layer = sel != wxNOT_FOUND ? m_layerCombo->GetString(sel) : "";
|
||||
m_layerLabel->SetLabel("Selected: " + layer);
|
||||
Log("Layer changed to: " + layer);
|
||||
}
|
||||
|
||||
void OnFontChanged(wxCommandEvent& event)
|
||||
{
|
||||
int sel = m_fontCombo->GetSelection();
|
||||
wxString font = sel != wxNOT_FOUND ? m_fontCombo->GetString(sel) : "";
|
||||
m_fontLabel->SetLabel("Selected: " + font);
|
||||
Log("Font changed to: " + font);
|
||||
}
|
||||
|
||||
void OnIconChanged(wxCommandEvent& event)
|
||||
{
|
||||
int sel = m_iconCombo->GetSelection();
|
||||
wxString component = sel != wxNOT_FOUND ? m_iconCombo->GetString(sel) : "";
|
||||
m_iconLabel->SetLabel("Selected: " + component);
|
||||
Log("Component changed to: " + component);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
LayerComboBox* m_layerCombo;
|
||||
FontComboBox* m_fontCombo;
|
||||
IconComboBox* m_iconCombo;
|
||||
wxStaticText* m_layerLabel;
|
||||
wxStaticText* m_fontLabel;
|
||||
wxStaticText* m_iconLabel;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class OwnerDrawnApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
OwnerDrawnFrame* frame = new OwnerDrawnFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(OwnerDrawnApp);
|
||||
205
tests/apps/standalone/pickers/pickers_test.cpp
Normal file
205
tests/apps/standalone/pickers/pickers_test.cpp
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
// wxColourPickerCtrl/wxFontPickerCtrl Test - Tests picker controls in WASM
|
||||
// KiCad uses these for color preferences, layer colors, and font selection
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/clrpicker.h"
|
||||
#include "wx/fontpicker.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class PickersTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class PickersTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
PickersTestFrame();
|
||||
|
||||
private:
|
||||
wxColourPickerCtrl* m_colourPicker1;
|
||||
wxColourPickerCtrl* m_colourPicker2;
|
||||
wxColourPickerCtrl* m_colourPicker3;
|
||||
wxFontPickerCtrl* m_fontPicker;
|
||||
wxPanel* m_previewPanel;
|
||||
wxStaticText* m_fontPreview;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void UpdatePreview();
|
||||
|
||||
void OnColourChanged(wxColourPickerEvent& evt);
|
||||
void OnFontChanged(wxFontPickerEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_COLOUR_PICKER_1 = wxID_HIGHEST + 1,
|
||||
ID_COLOUR_PICKER_2,
|
||||
ID_COLOUR_PICKER_3,
|
||||
ID_FONT_PICKER
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(PickersTestFrame, wxFrame)
|
||||
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_1, PickersTestFrame::OnColourChanged)
|
||||
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_2, PickersTestFrame::OnColourChanged)
|
||||
EVT_COLOURPICKER_CHANGED(ID_COLOUR_PICKER_3, PickersTestFrame::OnColourChanged)
|
||||
EVT_FONTPICKER_CHANGED(ID_FONT_PICKER, PickersTestFrame::OnFontChanged)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(PickersTestApp);
|
||||
|
||||
bool PickersTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
PickersTestFrame* frame = new PickersTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
PickersTestFrame::PickersTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxPicker Controls WASM Test",
|
||||
wxDefaultPosition, wxSize(700, 600))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"Picker Controls Test\n\n"
|
||||
"KiCad uses wxColourPickerCtrl for layer colors and color preferences.\n"
|
||||
"wxFontPickerCtrl is used for text/label font settings.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Color pickers section (KiCad layer colors)
|
||||
wxStaticBoxSizer* colourBox = new wxStaticBoxSizer(wxVERTICAL, this, "Layer Colors (wxColourPickerCtrl)");
|
||||
|
||||
wxFlexGridSizer* colourGrid = new wxFlexGridSizer(3, 3, 10, 20);
|
||||
colourGrid->AddGrowableCol(1);
|
||||
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Front Copper (F.Cu):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_colourPicker1 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_1, wxColour(255, 0, 0));
|
||||
colourGrid->Add(m_colourPicker1, 0, wxEXPAND);
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#FF0000"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Back Copper (B.Cu):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_colourPicker2 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_2, wxColour(0, 0, 255));
|
||||
colourGrid->Add(m_colourPicker2, 0, wxEXPAND);
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#0000FF"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "Silkscreen (F.SilkS):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_colourPicker3 = new wxColourPickerCtrl(this, ID_COLOUR_PICKER_3, wxColour(255, 255, 255));
|
||||
colourGrid->Add(m_colourPicker3, 0, wxEXPAND);
|
||||
colourGrid->Add(new wxStaticText(this, wxID_ANY, "#FFFFFF"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
colourBox->Add(colourGrid, 0, wxEXPAND | wxALL, 10);
|
||||
mainSizer->Add(colourBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Color preview panel
|
||||
wxStaticBoxSizer* previewBox = new wxStaticBoxSizer(wxVERTICAL, this, "Color Preview");
|
||||
m_previewPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 60));
|
||||
m_previewPanel->SetBackgroundColour(wxColour(50, 50, 50));
|
||||
previewBox->Add(m_previewPanel, 0, wxEXPAND | wxALL, 10);
|
||||
mainSizer->Add(previewBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Font picker section
|
||||
wxStaticBoxSizer* fontBox = new wxStaticBoxSizer(wxVERTICAL, this, "Text Font (wxFontPickerCtrl)");
|
||||
|
||||
wxBoxSizer* fontRow = new wxBoxSizer(wxHORIZONTAL);
|
||||
fontRow->Add(new wxStaticText(this, wxID_ANY, "Schematic Text Font:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 10);
|
||||
m_fontPicker = new wxFontPickerCtrl(this, ID_FONT_PICKER,
|
||||
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL),
|
||||
wxDefaultPosition, wxDefaultSize, wxFNTP_DEFAULT_STYLE);
|
||||
fontRow->Add(m_fontPicker, 1, wxEXPAND);
|
||||
fontBox->Add(fontRow, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Font preview
|
||||
m_fontPreview = new wxStaticText(this, wxID_ANY, "Sample Text: KiCad WASM Port - R1 10k VCC GND");
|
||||
m_fontPreview->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
fontBox->Add(m_fontPreview, 0, wxALL, 10);
|
||||
|
||||
mainSizer->Add(fontBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - Picker controls test");
|
||||
|
||||
UpdatePreview();
|
||||
LogEvent("Picker controls test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PICKERS_TEST] wxPicker controls test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void PickersTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PICKERS_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void PickersTestFrame::UpdatePreview()
|
||||
{
|
||||
// Update preview panel with gradient of selected colors
|
||||
// For simplicity, just use the first color as background
|
||||
if (m_previewPanel && m_colourPicker1) {
|
||||
m_previewPanel->SetBackgroundColour(m_colourPicker1->GetColour());
|
||||
m_previewPanel->Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void PickersTestFrame::OnColourChanged(wxColourPickerEvent& evt)
|
||||
{
|
||||
wxColour col = evt.GetColour();
|
||||
wxString hexColor = wxString::Format("#%02X%02X%02X", col.Red(), col.Green(), col.Blue());
|
||||
|
||||
wxString pickerName;
|
||||
switch (evt.GetId()) {
|
||||
case ID_COLOUR_PICKER_1: pickerName = "Front Copper"; break;
|
||||
case ID_COLOUR_PICKER_2: pickerName = "Back Copper"; break;
|
||||
case ID_COLOUR_PICKER_3: pickerName = "Silkscreen"; break;
|
||||
default: pickerName = "Unknown"; break;
|
||||
}
|
||||
|
||||
LogEvent(wxString::Format("Color changed: %s = %s", pickerName, hexColor));
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
void PickersTestFrame::OnFontChanged(wxFontPickerEvent& evt)
|
||||
{
|
||||
wxFont font = evt.GetFont();
|
||||
m_fontPreview->SetFont(font);
|
||||
m_fontPreview->Refresh();
|
||||
|
||||
LogEvent(wxString::Format("Font changed: %s, %dpt, %s",
|
||||
font.GetFaceName(),
|
||||
font.GetPointSize(),
|
||||
font.GetWeight() == wxFONTWEIGHT_BOLD ? "Bold" : "Normal"));
|
||||
}
|
||||
364
tests/apps/standalone/popup/popup_test.cpp
Normal file
364
tests/apps/standalone/popup/popup_test.cpp
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// wxPopupWindow Test - Transient popups like KiCad's toolbar palettes and status popups
|
||||
// Tests: wxPopupWindow, wxPopupTransientWindow, positioning, auto-dismiss
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/popupwin.h"
|
||||
|
||||
// Simple popup window (like KiCad STATUS_POPUP)
|
||||
class StatusPopup : public wxPopupWindow
|
||||
{
|
||||
public:
|
||||
StatusPopup(wxWindow* parent, const wxString& message)
|
||||
: wxPopupWindow(parent, wxBORDER_SIMPLE)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
panel->SetBackgroundColour(wxColour(255, 255, 200)); // Light yellow
|
||||
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* text = new wxStaticText(panel, wxID_ANY, message);
|
||||
text->SetFont(text->GetFont().Bold());
|
||||
sizer->Add(text, 0, wxALL, 8);
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
sizer->Fit(panel);
|
||||
SetClientSize(panel->GetSize());
|
||||
}
|
||||
};
|
||||
|
||||
// Transient popup with buttons (like KiCad ACTION_TOOLBAR_PALETTE)
|
||||
class ToolPalettePopup : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
ToolPalettePopup(wxWindow* parent, wxTextCtrl* log)
|
||||
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE), m_log(log)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
panel->SetBackgroundColour(wxColour(240, 240, 240));
|
||||
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* title = new wxStaticText(panel, wxID_ANY, "Tool Palette");
|
||||
title->SetFont(title->GetFont().Bold());
|
||||
mainSizer->Add(title, 0, wxALL, 5);
|
||||
|
||||
// Tool buttons
|
||||
wxGridSizer* buttonGrid = new wxGridSizer(3, 3, 2, 2);
|
||||
|
||||
for (int i = 1; i <= 9; i++)
|
||||
{
|
||||
wxButton* btn = new wxButton(panel, 1000 + i,
|
||||
wxString::Format("T%d", i),
|
||||
wxDefaultPosition, wxSize(40, 40));
|
||||
btn->Bind(wxEVT_BUTTON, &ToolPalettePopup::OnToolClick, this);
|
||||
buttonGrid->Add(btn, 0);
|
||||
}
|
||||
|
||||
mainSizer->Add(buttonGrid, 0, wxALL, 5);
|
||||
|
||||
panel->SetSizer(mainSizer);
|
||||
mainSizer->Fit(panel);
|
||||
SetClientSize(panel->GetSize());
|
||||
}
|
||||
|
||||
private:
|
||||
void OnToolClick(wxCommandEvent& event)
|
||||
{
|
||||
int toolNum = event.GetId() - 1000;
|
||||
m_log->AppendText(wxString::Format("Tool %d clicked\n", toolNum));
|
||||
Dismiss(); // Close popup after selection
|
||||
}
|
||||
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
// Color picker popup (like KiCad color pickers)
|
||||
class ColorPickerPopup : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
ColorPickerPopup(wxWindow* parent, wxTextCtrl* log, wxPanel* swatch)
|
||||
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE), m_log(log), m_swatch(swatch)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
panel->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* title = new wxStaticText(panel, wxID_ANY, "Select Color");
|
||||
mainSizer->Add(title, 0, wxALL, 5);
|
||||
|
||||
// Color grid
|
||||
wxGridSizer* colorGrid = new wxGridSizer(4, 4, 2, 2);
|
||||
|
||||
wxColour colors[] = {
|
||||
*wxRED, wxColour(255, 128, 0), *wxYELLOW, wxColour(128, 255, 0),
|
||||
*wxGREEN, wxColour(0, 255, 128), *wxCYAN, wxColour(0, 128, 255),
|
||||
*wxBLUE, wxColour(128, 0, 255), wxColour(255, 0, 255), wxColour(255, 0, 128),
|
||||
*wxBLACK, wxColour(64, 64, 64), wxColour(128, 128, 128), *wxWHITE
|
||||
};
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
wxPanel* colorBtn = new wxPanel(panel, 2000 + i, wxDefaultPosition, wxSize(30, 30));
|
||||
colorBtn->SetBackgroundColour(colors[i]);
|
||||
colorBtn->Bind(wxEVT_LEFT_DOWN, &ColorPickerPopup::OnColorClick, this);
|
||||
colorGrid->Add(colorBtn, 0);
|
||||
}
|
||||
|
||||
mainSizer->Add(colorGrid, 0, wxALL, 5);
|
||||
|
||||
panel->SetSizer(mainSizer);
|
||||
mainSizer->Fit(panel);
|
||||
SetClientSize(panel->GetSize());
|
||||
|
||||
// Store colors for lookup
|
||||
for (int i = 0; i < 16; i++)
|
||||
m_colors[i] = colors[i];
|
||||
}
|
||||
|
||||
private:
|
||||
void OnColorClick(wxMouseEvent& event)
|
||||
{
|
||||
wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject());
|
||||
if (panel)
|
||||
{
|
||||
int colorIdx = panel->GetId() - 2000;
|
||||
wxColour color = m_colors[colorIdx];
|
||||
m_swatch->SetBackgroundColour(color);
|
||||
m_swatch->Refresh();
|
||||
m_log->AppendText(wxString::Format("Color selected: RGB(%d,%d,%d)\n",
|
||||
color.Red(), color.Green(), color.Blue()));
|
||||
Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
wxTextCtrl* m_log;
|
||||
wxPanel* m_swatch;
|
||||
wxColour m_colors[16];
|
||||
};
|
||||
|
||||
class PopupFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
PopupFrame() : wxFrame(nullptr, wxID_ANY, "wxPopupWindow Test",
|
||||
wxDefaultPosition, wxSize(700, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses popup windows for toolbar palettes and status messages.\n"
|
||||
"Tests: wxPopupWindow, wxPopupTransientWindow, positioning, dismiss.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Status popup section
|
||||
wxStaticBoxSizer* statusSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Status Popup (stays until dismissed)");
|
||||
|
||||
m_showStatusBtn = new wxButton(mainPanel, wxID_ANY, "Show Status Popup");
|
||||
m_showStatusBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowStatusPopup, this);
|
||||
statusSizer->Add(m_showStatusBtn, 0, wxALL, 5);
|
||||
|
||||
m_hideStatusBtn = new wxButton(mainPanel, wxID_ANY, "Hide Status Popup");
|
||||
m_hideStatusBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnHideStatusPopup, this);
|
||||
m_hideStatusBtn->Enable(false);
|
||||
statusSizer->Add(m_hideStatusBtn, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(statusSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Tool palette section
|
||||
wxStaticBoxSizer* paletteSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Tool Palette (transient - click outside to dismiss)");
|
||||
|
||||
wxButton* showPaletteBtn = new wxButton(mainPanel, wxID_ANY, "Show Tool Palette");
|
||||
showPaletteBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowToolPalette, this);
|
||||
paletteSizer->Add(showPaletteBtn, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(paletteSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Color picker section
|
||||
wxStaticBoxSizer* colorSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Color Picker (transient popup)");
|
||||
|
||||
m_colorSwatch = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(40, 40));
|
||||
m_colorSwatch->SetBackgroundColour(*wxRED);
|
||||
colorSizer->Add(m_colorSwatch, 0, wxALL, 5);
|
||||
|
||||
wxButton* showColorBtn = new wxButton(mainPanel, wxID_ANY, "Pick Color...");
|
||||
showColorBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnShowColorPicker, this);
|
||||
colorSizer->Add(showColorBtn, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
||||
mainSizer->Add(colorSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Positioning section
|
||||
wxStaticBoxSizer* posSizer = new wxStaticBoxSizer(wxHORIZONTAL, mainPanel, "Popup Positioning");
|
||||
|
||||
wxButton* posAboveBtn = new wxButton(mainPanel, wxID_ANY, "Popup Above Me");
|
||||
posAboveBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupAbove, this);
|
||||
posSizer->Add(posAboveBtn, 0, wxALL, 5);
|
||||
|
||||
wxButton* posBelowBtn = new wxButton(mainPanel, wxID_ANY, "Popup Below Me");
|
||||
posBelowBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupBelow, this);
|
||||
posSizer->Add(posBelowBtn, 0, wxALL, 5);
|
||||
|
||||
wxButton* posRightBtn = new wxButton(mainPanel, wxID_ANY, "Popup Right of Me");
|
||||
posRightBtn->Bind(wxEVT_BUTTON, &PopupFrame::OnPopupRight, this);
|
||||
posSizer->Add(posRightBtn, 0, wxALL, 5);
|
||||
|
||||
mainSizer->Add(posSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Popup window test app started");
|
||||
Log("Popup window test app started");
|
||||
}
|
||||
|
||||
~PopupFrame()
|
||||
{
|
||||
if (m_statusPopup)
|
||||
{
|
||||
m_statusPopup->Destroy();
|
||||
m_statusPopup = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void OnShowStatusPopup(wxCommandEvent& event)
|
||||
{
|
||||
if (!m_statusPopup)
|
||||
{
|
||||
m_statusPopup = new StatusPopup(this, "Status: Processing...\nPlease wait");
|
||||
wxPoint pos = m_showStatusBtn->GetScreenPosition();
|
||||
pos.y += m_showStatusBtn->GetSize().y + 5;
|
||||
m_statusPopup->SetPosition(pos);
|
||||
m_statusPopup->Show();
|
||||
m_hideStatusBtn->Enable(true);
|
||||
Log("Status popup shown");
|
||||
}
|
||||
}
|
||||
|
||||
void OnHideStatusPopup(wxCommandEvent& event)
|
||||
{
|
||||
if (m_statusPopup)
|
||||
{
|
||||
m_statusPopup->Hide();
|
||||
m_statusPopup->Destroy();
|
||||
m_statusPopup = nullptr;
|
||||
m_hideStatusBtn->Enable(false);
|
||||
Log("Status popup hidden");
|
||||
}
|
||||
}
|
||||
|
||||
void OnShowToolPalette(wxCommandEvent& event)
|
||||
{
|
||||
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
|
||||
ToolPalettePopup* popup = new ToolPalettePopup(this, m_log);
|
||||
|
||||
wxPoint pos = btn->GetScreenPosition();
|
||||
pos.y += btn->GetSize().y + 5;
|
||||
popup->SetPosition(pos);
|
||||
popup->Popup();
|
||||
Log("Tool palette shown (click outside to dismiss)");
|
||||
}
|
||||
|
||||
void OnShowColorPicker(wxCommandEvent& event)
|
||||
{
|
||||
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
|
||||
ColorPickerPopup* popup = new ColorPickerPopup(this, m_log, m_colorSwatch);
|
||||
|
||||
wxPoint pos = btn->GetScreenPosition();
|
||||
pos.y += btn->GetSize().y + 5;
|
||||
popup->SetPosition(pos);
|
||||
popup->Popup();
|
||||
Log("Color picker shown");
|
||||
}
|
||||
|
||||
void OnPopupAbove(wxCommandEvent& event)
|
||||
{
|
||||
ShowPositionedPopup(event, 0, -1);
|
||||
}
|
||||
|
||||
void OnPopupBelow(wxCommandEvent& event)
|
||||
{
|
||||
ShowPositionedPopup(event, 0, 1);
|
||||
}
|
||||
|
||||
void OnPopupRight(wxCommandEvent& event)
|
||||
{
|
||||
ShowPositionedPopup(event, 1, 0);
|
||||
}
|
||||
|
||||
void ShowPositionedPopup(wxCommandEvent& event, int xDir, int yDir)
|
||||
{
|
||||
wxButton* btn = dynamic_cast<wxButton*>(event.GetEventObject());
|
||||
|
||||
class SimpleTransientPopup : public wxPopupTransientWindow
|
||||
{
|
||||
public:
|
||||
SimpleTransientPopup(wxWindow* parent, const wxString& msg)
|
||||
: wxPopupTransientWindow(parent, wxBORDER_SIMPLE)
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
panel->SetBackgroundColour(wxColour(200, 220, 255));
|
||||
wxStaticText* text = new wxStaticText(panel, wxID_ANY, msg);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(text, 0, wxALL, 10);
|
||||
panel->SetSizer(sizer);
|
||||
sizer->Fit(panel);
|
||||
SetClientSize(panel->GetSize());
|
||||
}
|
||||
};
|
||||
|
||||
wxString direction;
|
||||
if (yDir < 0) direction = "Above";
|
||||
else if (yDir > 0) direction = "Below";
|
||||
else if (xDir > 0) direction = "Right";
|
||||
|
||||
SimpleTransientPopup* popup = new SimpleTransientPopup(this, "Popup " + direction + "!");
|
||||
|
||||
wxPoint pos = btn->GetScreenPosition();
|
||||
wxSize btnSize = btn->GetSize();
|
||||
wxSize popupSize = popup->GetSize();
|
||||
|
||||
if (yDir < 0)
|
||||
pos.y -= popupSize.y + 5;
|
||||
else if (yDir > 0)
|
||||
pos.y += btnSize.y + 5;
|
||||
|
||||
if (xDir > 0)
|
||||
pos.x += btnSize.x + 5;
|
||||
|
||||
popup->SetPosition(pos);
|
||||
popup->Popup();
|
||||
Log("Positioned popup shown " + direction.Lower());
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxButton* m_showStatusBtn;
|
||||
wxButton* m_hideStatusBtn;
|
||||
wxPanel* m_colorSwatch;
|
||||
wxTextCtrl* m_log;
|
||||
StatusPopup* m_statusPopup = nullptr;
|
||||
};
|
||||
|
||||
class PopupApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
PopupFrame* frame = new PopupFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(PopupApp);
|
||||
496
tests/apps/standalone/print/print_test.cpp
Normal file
496
tests/apps/standalone/print/print_test.cpp
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
// wxPrinting Test - Tests print functionality in WASM
|
||||
// KiCad uses printing for schematic/PCB output
|
||||
//
|
||||
// Tests:
|
||||
// - wxPrintout callbacks (OnPrintPage, OnBeginPrinting, etc.)
|
||||
// - wxPrintPreview rendering
|
||||
// - wxPrinter::Print() triggering
|
||||
// - window.print() browser integration
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#if wxUSE_PRINTING_ARCHITECTURE
|
||||
|
||||
#include "wx/print.h"
|
||||
#include "wx/printdlg.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
// Forward declarations
|
||||
class PrintTestApp;
|
||||
class PrintTestFrame;
|
||||
class TestPrintout;
|
||||
|
||||
// Global print data
|
||||
static wxPrintData* g_printData = nullptr;
|
||||
static wxPageSetupDialogData* g_pageSetupData = nullptr;
|
||||
|
||||
// ============================================================
|
||||
// TestPrintout - Simple printable document (2 pages)
|
||||
// ============================================================
|
||||
class TestPrintout : public wxPrintout
|
||||
{
|
||||
public:
|
||||
TestPrintout(const wxString& title = "Test Printout")
|
||||
: wxPrintout(title) {}
|
||||
|
||||
bool OnPrintPage(int page) override;
|
||||
bool HasPage(int page) override { return page >= 1 && page <= 2; }
|
||||
void GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo) override;
|
||||
bool OnBeginDocument(int startPage, int endPage) override;
|
||||
void OnEndDocument() override;
|
||||
void OnBeginPrinting() override;
|
||||
void OnEndPrinting() override;
|
||||
|
||||
private:
|
||||
void DrawPageOne(wxDC* dc);
|
||||
void DrawPageTwo(wxDC* dc);
|
||||
void LogEvent(const wxString& msg);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// PrintTestFrame - Main test window
|
||||
// ============================================================
|
||||
class PrintTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
PrintTestFrame();
|
||||
~PrintTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
wxPanel* m_previewPanel;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void DrawDocument(wxDC& dc);
|
||||
|
||||
// Event handlers
|
||||
void OnPrintPreview(wxCommandEvent& evt);
|
||||
void OnPrint(wxCommandEvent& evt);
|
||||
void OnBrowserPrint(wxCommandEvent& evt);
|
||||
void OnPageSetup(wxCommandEvent& evt);
|
||||
void OnPreviewPanelPaint(wxPaintEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// PrintTestApp
|
||||
// ============================================================
|
||||
class PrintTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
bool OnInit() override;
|
||||
int OnExit() override;
|
||||
};
|
||||
|
||||
// IDs
|
||||
enum {
|
||||
ID_PRINT_PREVIEW = wxID_HIGHEST + 1,
|
||||
ID_PRINT,
|
||||
ID_BROWSER_PRINT,
|
||||
ID_PAGE_SETUP,
|
||||
ID_PREVIEW_PANEL
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(PrintTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_PRINT_PREVIEW, PrintTestFrame::OnPrintPreview)
|
||||
EVT_BUTTON(ID_PRINT, PrintTestFrame::OnPrint)
|
||||
EVT_BUTTON(ID_BROWSER_PRINT, PrintTestFrame::OnBrowserPrint)
|
||||
EVT_BUTTON(ID_PAGE_SETUP, PrintTestFrame::OnPageSetup)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(PrintTestApp);
|
||||
|
||||
// ============================================================
|
||||
// App Implementation
|
||||
// ============================================================
|
||||
|
||||
bool PrintTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
// Initialize print data
|
||||
g_printData = new wxPrintData;
|
||||
g_pageSetupData = new wxPageSetupDialogData;
|
||||
(*g_pageSetupData) = *g_printData;
|
||||
g_pageSetupData->SetMarginTopLeft(wxPoint(15, 15));
|
||||
g_pageSetupData->SetMarginBottomRight(wxPoint(15, 15));
|
||||
|
||||
PrintTestFrame* frame = new PrintTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
int PrintTestApp::OnExit()
|
||||
{
|
||||
delete g_printData;
|
||||
delete g_pageSetupData;
|
||||
g_printData = nullptr;
|
||||
g_pageSetupData = nullptr;
|
||||
return wxApp::OnExit();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Frame Implementation
|
||||
// ============================================================
|
||||
|
||||
PrintTestFrame::PrintTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxPrinting WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxPrinting Test\n\n"
|
||||
"KiCad uses wxPrinting for schematic and PCB printing.\n"
|
||||
"Test print preview, print dialog, and browser print integration.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_PRINT_PREVIEW, "Print Preview"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_PRINT, "Print..."), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_BROWSER_PRINT, "Browser Print"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_PAGE_SETUP, "Page Setup"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
// Preview panel - shows what will be printed
|
||||
wxStaticBoxSizer* previewBox = new wxStaticBoxSizer(wxVERTICAL, this, "Document Preview");
|
||||
m_previewPanel = new wxPanel(this, ID_PREVIEW_PANEL, wxDefaultPosition, wxSize(-1, 200));
|
||||
m_previewPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_previewPanel->Bind(wxEVT_PAINT, &PrintTestFrame::OnPreviewPanelPaint, this);
|
||||
previewBox->Add(m_previewPanel, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(previewBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
LogEvent("Print test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PRINT_TEST] wxPrinting test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
PrintTestFrame::~PrintTestFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void PrintTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
if (m_log)
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PRINT_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrintTestFrame::DrawDocument(wxDC& dc)
|
||||
{
|
||||
// Draw sample content that will be printed
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
dc.SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc.DrawText("Print Test Document - Page 1", 20, 20);
|
||||
|
||||
dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc.DrawText("This is a test document for WASM printing.", 20, 50);
|
||||
|
||||
// Draw shapes
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
|
||||
dc.DrawRectangle(20, 80, 150, 80);
|
||||
dc.DrawText("Rectangle", 60, 110);
|
||||
|
||||
dc.SetBrush(*wxCYAN_BRUSH);
|
||||
dc.DrawCircle(280, 120, 40);
|
||||
dc.DrawText("Circle", 260, 115);
|
||||
|
||||
dc.SetPen(wxPen(*wxRED, 2));
|
||||
dc.DrawLine(20, 180, 350, 180);
|
||||
dc.DrawText("Red Line", 160, 185);
|
||||
}
|
||||
|
||||
void PrintTestFrame::OnPreviewPanelPaint(wxPaintEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxPaintDC dc(m_previewPanel);
|
||||
DrawDocument(dc);
|
||||
}
|
||||
|
||||
void PrintTestFrame::OnPrintPreview(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Print Preview...");
|
||||
|
||||
wxPrintDialogData printDialogData(*g_printData);
|
||||
|
||||
// Create two printouts: one for preview, one for printing from preview
|
||||
wxPrintPreview* preview = new wxPrintPreview(
|
||||
new TestPrintout("Preview"),
|
||||
new TestPrintout("Print"),
|
||||
&printDialogData
|
||||
);
|
||||
|
||||
if (!preview->IsOk())
|
||||
{
|
||||
delete preview;
|
||||
LogEvent("ERROR: Print preview initialization failed");
|
||||
wxMessageBox("Print preview failed to initialize.",
|
||||
"Print Preview Error", wxOK | wxICON_ERROR, this);
|
||||
return;
|
||||
}
|
||||
|
||||
wxPreviewFrame* frame = new wxPreviewFrame(
|
||||
preview, this, "Print Preview"
|
||||
);
|
||||
frame->InitializeWithModality(wxPreviewFrame_NonModal);
|
||||
frame->Centre(wxBOTH);
|
||||
frame->Show();
|
||||
|
||||
LogEvent("Print Preview frame shown");
|
||||
}
|
||||
|
||||
void PrintTestFrame::OnPrint(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Print dialog...");
|
||||
|
||||
wxPrintDialogData printDialogData(*g_printData);
|
||||
wxPrinter printer(&printDialogData);
|
||||
TestPrintout printout("Test Print");
|
||||
|
||||
if (!printer.Print(this, &printout, true))
|
||||
{
|
||||
if (wxPrinter::GetLastError() == wxPRINTER_ERROR)
|
||||
{
|
||||
LogEvent("ERROR: Printing failed - printer error");
|
||||
wxMessageBox("Printing failed. Printer may not be configured correctly.",
|
||||
"Print Error", wxOK | wxICON_ERROR, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogEvent("Print cancelled by user");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(*g_printData) = printer.GetPrintDialogData().GetPrintData();
|
||||
LogEvent("Print completed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTestFrame::OnBrowserPrint(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Triggering browser print dialog...");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PRINT_EVENT] Calling window.print() for browser printing');
|
||||
// In WASM, this triggers the browser's native print dialog
|
||||
// Users can "Save as PDF" from there
|
||||
window.print();
|
||||
});
|
||||
LogEvent("Browser print dialog triggered (window.print called)");
|
||||
#else
|
||||
LogEvent("Browser print only available in WASM build");
|
||||
wxMessageBox("Browser print is only available in WASM builds.",
|
||||
"Not Available", wxOK | wxICON_INFORMATION, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrintTestFrame::OnPageSetup(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Opening Page Setup dialog...");
|
||||
|
||||
(*g_pageSetupData) = *g_printData;
|
||||
|
||||
wxPageSetupDialog pageSetupDialog(this, g_pageSetupData);
|
||||
if (pageSetupDialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
(*g_printData) = pageSetupDialog.GetPageSetupDialogData().GetPrintData();
|
||||
(*g_pageSetupData) = pageSetupDialog.GetPageSetupDialogData();
|
||||
LogEvent("Page setup completed");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogEvent("Page setup cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TestPrintout Implementation
|
||||
// ============================================================
|
||||
|
||||
void TestPrintout::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PRINTOUT_CALLBACK] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestPrintout::OnBeginPrinting()
|
||||
{
|
||||
LogEvent("OnBeginPrinting called");
|
||||
wxPrintout::OnBeginPrinting();
|
||||
}
|
||||
|
||||
void TestPrintout::OnEndPrinting()
|
||||
{
|
||||
LogEvent("OnEndPrinting called");
|
||||
wxPrintout::OnEndPrinting();
|
||||
}
|
||||
|
||||
bool TestPrintout::OnBeginDocument(int startPage, int endPage)
|
||||
{
|
||||
LogEvent(wxString::Format("OnBeginDocument: pages %d to %d", startPage, endPage));
|
||||
return wxPrintout::OnBeginDocument(startPage, endPage);
|
||||
}
|
||||
|
||||
void TestPrintout::OnEndDocument()
|
||||
{
|
||||
LogEvent("OnEndDocument called");
|
||||
wxPrintout::OnEndDocument();
|
||||
}
|
||||
|
||||
void TestPrintout::GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo)
|
||||
{
|
||||
*minPage = 1;
|
||||
*maxPage = 2;
|
||||
*selPageFrom = 1;
|
||||
*selPageTo = 2;
|
||||
LogEvent("GetPageInfo: 2 pages available");
|
||||
}
|
||||
|
||||
bool TestPrintout::OnPrintPage(int page)
|
||||
{
|
||||
LogEvent(wxString::Format("OnPrintPage: printing page %d", page));
|
||||
|
||||
wxDC* dc = GetDC();
|
||||
if (!dc)
|
||||
{
|
||||
LogEvent("ERROR: No DC available for printing");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page == 1)
|
||||
DrawPageOne(dc);
|
||||
else if (page == 2)
|
||||
DrawPageTwo(dc);
|
||||
else
|
||||
return false;
|
||||
|
||||
// Draw page number
|
||||
MapScreenSizeToPage();
|
||||
dc->SetFont(wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc->DrawText(wxString::Format("Page %d of 2", page), 10, 10);
|
||||
|
||||
LogEvent(wxString::Format("Page %d drawn successfully", page));
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestPrintout::DrawPageOne(wxDC* dc)
|
||||
{
|
||||
LogEvent("DrawPageOne: drawing content");
|
||||
|
||||
// Scale to fit page
|
||||
FitThisSizeToPage(wxSize(400, 300));
|
||||
|
||||
dc->SetBackground(*wxWHITE_BRUSH);
|
||||
|
||||
dc->SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc->DrawText("WASM Print Test - Page 1", 50, 50);
|
||||
|
||||
dc->SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc->DrawText("This tests wxPrinting in WebAssembly.", 50, 80);
|
||||
dc->DrawText("KiCad uses this for schematic/PCB printing.", 50, 100);
|
||||
|
||||
// Shapes
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
|
||||
dc->DrawRectangle(50, 130, 150, 80);
|
||||
dc->DrawText("Rectangle 150x80", 70, 160);
|
||||
|
||||
dc->SetBrush(*wxCYAN_BRUSH);
|
||||
dc->DrawCircle(300, 170, 40);
|
||||
dc->DrawText("r=40", 285, 165);
|
||||
|
||||
dc->SetPen(wxPen(*wxRED, 2));
|
||||
dc->DrawLine(50, 230, 350, 230);
|
||||
}
|
||||
|
||||
void TestPrintout::DrawPageTwo(wxDC* dc)
|
||||
{
|
||||
LogEvent("DrawPageTwo: drawing content");
|
||||
|
||||
// Scale to fit page
|
||||
FitThisSizeToPage(wxSize(400, 300));
|
||||
|
||||
dc->SetBackground(*wxWHITE_BRUSH);
|
||||
|
||||
dc->SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc->DrawText("WASM Print Test - Page 2", 50, 50);
|
||||
|
||||
dc->SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc->DrawText("Second page demonstrates multi-page printing.", 50, 80);
|
||||
|
||||
// Draw grid pattern
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
for (int x = 50; x <= 350; x += 30)
|
||||
{
|
||||
dc->DrawLine(x, 120, x, 220);
|
||||
}
|
||||
for (int y = 120; y <= 220; y += 20)
|
||||
{
|
||||
dc->DrawLine(50, y, 350, y);
|
||||
}
|
||||
dc->DrawText("Grid Pattern", 170, 230);
|
||||
|
||||
// Draw some text
|
||||
dc->SetFont(wxFont(8, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc->DrawText("Monospace font test: 0123456789", 50, 260);
|
||||
}
|
||||
|
||||
#else // !wxUSE_PRINTING_ARCHITECTURE
|
||||
|
||||
// Fallback if printing is not enabled
|
||||
#include "wx/wx.h"
|
||||
|
||||
class PrintTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
bool OnInit() override
|
||||
{
|
||||
wxMessageBox("wxUSE_PRINTING_ARCHITECTURE is not enabled.\n"
|
||||
"Printing support is not available.",
|
||||
"Print Test Error", wxOK | wxICON_ERROR);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(PrintTestApp);
|
||||
|
||||
#endif // wxUSE_PRINTING_ARCHITECTURE
|
||||
324
tests/apps/standalone/printpreview/printpreview_test.cpp
Normal file
324
tests/apps/standalone/printpreview/printpreview_test.cpp
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
// wxPrintPreview Test - Print preview system
|
||||
// Tests wxPreviewFrame, wxPrintout, wxPrintData for KiCad's print functionality
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/print.h"
|
||||
#include "wx/printdlg.h"
|
||||
#include "wx/dcmemory.h"
|
||||
|
||||
// Custom Printout class - simulates KiCad schematic/PCB print
|
||||
class SamplePrintout : public wxPrintout
|
||||
{
|
||||
public:
|
||||
SamplePrintout(const wxString& title = "Sample Printout") : wxPrintout(title) {}
|
||||
|
||||
virtual bool OnPrintPage(int page) override
|
||||
{
|
||||
wxDC* dc = GetDC();
|
||||
if (!dc) return false;
|
||||
|
||||
// Get page size
|
||||
int pageW, pageH;
|
||||
GetPageSizePixels(&pageW, &pageH);
|
||||
|
||||
// Draw frame
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
dc->SetBrush(*wxWHITE_BRUSH);
|
||||
dc->DrawRectangle(10, 10, pageW - 20, pageH - 20);
|
||||
|
||||
// Draw title block (like KiCad)
|
||||
int titleBlockH = 80;
|
||||
dc->DrawRectangle(10, pageH - titleBlockH - 10, pageW - 20, titleBlockH);
|
||||
|
||||
// Draw grid lines
|
||||
dc->SetPen(wxPen(*wxLIGHT_GREY, 1, wxPENSTYLE_DOT));
|
||||
int gridSize = 50;
|
||||
for (int x = gridSize; x < pageW - gridSize; x += gridSize)
|
||||
{
|
||||
dc->DrawLine(x, 10, x, pageH - titleBlockH - 10);
|
||||
}
|
||||
for (int y = gridSize; y < pageH - titleBlockH - gridSize; y += gridSize)
|
||||
{
|
||||
dc->DrawLine(10, y, pageW - 10, y);
|
||||
}
|
||||
|
||||
// Draw some "components" (circles and rectangles)
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
dc->SetBrush(*wxRED_BRUSH);
|
||||
dc->DrawCircle(pageW / 4, pageH / 3, 30);
|
||||
|
||||
dc->SetBrush(*wxBLUE_BRUSH);
|
||||
dc->DrawRectangle(pageW / 2, pageH / 3 - 20, 60, 40);
|
||||
|
||||
dc->SetBrush(*wxGREEN_BRUSH);
|
||||
dc->DrawCircle(3 * pageW / 4, pageH / 3, 25);
|
||||
|
||||
// Draw "wires" connecting them
|
||||
dc->SetPen(wxPen(*wxBLACK, 2));
|
||||
dc->DrawLine(pageW / 4 + 30, pageH / 3, pageW / 2, pageH / 3);
|
||||
dc->DrawLine(pageW / 2 + 60, pageH / 3, 3 * pageW / 4 - 25, pageH / 3);
|
||||
|
||||
// Title block text
|
||||
dc->SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc->DrawText("KiCad Print Preview Test", 20, pageH - titleBlockH);
|
||||
dc->SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc->DrawText(wxString::Format("Page %d of 1", page), 20, pageH - titleBlockH + 20);
|
||||
dc->DrawText("Date: 2025-12-03", 20, pageH - titleBlockH + 35);
|
||||
dc->DrawText("Rev: 1.0", 20, pageH - titleBlockH + 50);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool HasPage(int pageNum) override
|
||||
{
|
||||
return pageNum == 1;
|
||||
}
|
||||
|
||||
virtual void GetPageInfo(int* minPage, int* maxPage, int* selPageFrom, int* selPageTo) override
|
||||
{
|
||||
if (minPage) *minPage = 1;
|
||||
if (maxPage) *maxPage = 1;
|
||||
if (selPageFrom) *selPageFrom = 1;
|
||||
if (selPageTo) *selPageTo = 1;
|
||||
}
|
||||
};
|
||||
|
||||
class PrintPreviewFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
PrintPreviewFrame() : wxFrame(nullptr, wxID_ANY, "wxPrintPreview Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxPrintout and wxPreviewFrame for print preview.\n"
|
||||
"Tests: Print preview, page setup dialog, print data persistence.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btnPreview = new wxButton(mainPanel, wxID_ANY, "Print Preview");
|
||||
wxButton* btnPageSetup = new wxButton(mainPanel, wxID_ANY, "Page Setup");
|
||||
wxButton* btnPrint = new wxButton(mainPanel, wxID_ANY, "Print...");
|
||||
|
||||
btnPreview->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPrintPreview, this);
|
||||
btnPageSetup->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPageSetup, this);
|
||||
btnPrint->Bind(wxEVT_BUTTON, &PrintPreviewFrame::OnPrint, this);
|
||||
|
||||
btnSizer->Add(btnPreview, 0, wxRIGHT, 5);
|
||||
btnSizer->Add(btnPageSetup, 0, wxRIGHT, 5);
|
||||
btnSizer->Add(btnPrint, 0);
|
||||
|
||||
mainSizer->Add(btnSizer, 0, wxALL, 5);
|
||||
|
||||
// Preview area (draws same content as printout)
|
||||
m_previewPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition,
|
||||
wxDefaultSize, wxBORDER_SUNKEN);
|
||||
m_previewPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_previewPanel->Bind(wxEVT_PAINT, &PrintPreviewFrame::OnPaintPreview, this);
|
||||
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Preview Area:"), 0, wxLEFT | wxTOP, 5);
|
||||
mainSizer->Add(m_previewPanel, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Settings display
|
||||
wxStaticBoxSizer* settingsSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Print Settings");
|
||||
m_settingsText = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition,
|
||||
wxSize(-1, 80), wxTE_MULTILINE | wxTE_READONLY);
|
||||
settingsSizer->Add(m_settingsText, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(settingsSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Initialize print data
|
||||
m_printData = new wxPrintData();
|
||||
m_pageSetupData = new wxPageSetupDialogData(*m_printData);
|
||||
|
||||
UpdateSettingsDisplay();
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Print preview test app started");
|
||||
Log("Print preview test app started");
|
||||
}
|
||||
|
||||
~PrintPreviewFrame()
|
||||
{
|
||||
delete m_printData;
|
||||
delete m_pageSetupData;
|
||||
}
|
||||
|
||||
private:
|
||||
void OnPrintPreview(wxCommandEvent& event)
|
||||
{
|
||||
Log("Opening print preview...");
|
||||
|
||||
// Create two printouts - one for preview, one for printing
|
||||
SamplePrintout* printoutPreview = new SamplePrintout("Preview");
|
||||
SamplePrintout* printoutPrint = new SamplePrintout("Print");
|
||||
|
||||
wxPrintPreview* preview = new wxPrintPreview(printoutPreview, printoutPrint, m_printData);
|
||||
|
||||
if (!preview->IsOk())
|
||||
{
|
||||
delete preview;
|
||||
Log("ERROR: Failed to create print preview");
|
||||
wxMessageBox("Failed to create print preview", "Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
wxPreviewFrame* frame = new wxPreviewFrame(preview, this, "Print Preview",
|
||||
wxDefaultPosition, wxSize(700, 500));
|
||||
frame->Centre();
|
||||
frame->Initialize();
|
||||
frame->Show();
|
||||
|
||||
Log("Print preview opened successfully");
|
||||
}
|
||||
|
||||
void OnPageSetup(wxCommandEvent& event)
|
||||
{
|
||||
Log("Opening page setup dialog...");
|
||||
|
||||
*m_pageSetupData = *m_printData;
|
||||
|
||||
wxPageSetupDialog pageSetupDialog(this, m_pageSetupData);
|
||||
|
||||
if (pageSetupDialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
*m_pageSetupData = pageSetupDialog.GetPageSetupDialogData();
|
||||
*m_printData = m_pageSetupData->GetPrintData();
|
||||
|
||||
Log("Page setup changed");
|
||||
UpdateSettingsDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Page setup cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrint(wxCommandEvent& event)
|
||||
{
|
||||
Log("Opening print dialog...");
|
||||
|
||||
wxPrintDialogData printDialogData(*m_printData);
|
||||
|
||||
wxPrintDialog printDialog(this, &printDialogData);
|
||||
|
||||
if (printDialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
*m_printData = printDialog.GetPrintDialogData().GetPrintData();
|
||||
Log("Print initiated (simulated in browser)");
|
||||
UpdateSettingsDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Print cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
void OnPaintPreview(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_previewPanel);
|
||||
|
||||
wxSize size = m_previewPanel->GetClientSize();
|
||||
|
||||
// Draw frame
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(*wxWHITE_BRUSH);
|
||||
dc.DrawRectangle(5, 5, size.x - 10, size.y - 10);
|
||||
|
||||
// Draw title block
|
||||
int titleBlockH = 40;
|
||||
dc.DrawRectangle(5, size.y - titleBlockH - 5, size.x - 10, titleBlockH);
|
||||
|
||||
// Draw grid
|
||||
dc.SetPen(wxPen(*wxLIGHT_GREY, 1, wxPENSTYLE_DOT));
|
||||
int gridSize = 30;
|
||||
for (int x = gridSize; x < size.x - gridSize; x += gridSize)
|
||||
{
|
||||
dc.DrawLine(x, 5, x, size.y - titleBlockH - 5);
|
||||
}
|
||||
for (int y = gridSize; y < size.y - titleBlockH - gridSize; y += gridSize)
|
||||
{
|
||||
dc.DrawLine(5, y, size.x - 5, y);
|
||||
}
|
||||
|
||||
// Draw components
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(*wxRED_BRUSH);
|
||||
dc.DrawCircle(size.x / 4, size.y / 3, 15);
|
||||
|
||||
dc.SetBrush(*wxBLUE_BRUSH);
|
||||
dc.DrawRectangle(size.x / 2, size.y / 3 - 10, 30, 20);
|
||||
|
||||
dc.SetBrush(*wxGREEN_BRUSH);
|
||||
dc.DrawCircle(3 * size.x / 4, size.y / 3, 12);
|
||||
|
||||
// Wires
|
||||
dc.SetPen(wxPen(*wxBLACK, 2));
|
||||
dc.DrawLine(size.x / 4 + 15, size.y / 3, size.x / 2, size.y / 3);
|
||||
dc.DrawLine(size.x / 2 + 30, size.y / 3, 3 * size.x / 4 - 12, size.y / 3);
|
||||
|
||||
// Title block text
|
||||
dc.SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
dc.DrawText("Sample Schematic", 10, size.y - titleBlockH + 5);
|
||||
}
|
||||
|
||||
void UpdateSettingsDisplay()
|
||||
{
|
||||
wxString settings;
|
||||
settings += wxString::Format("Orientation: %s\n",
|
||||
m_printData->GetOrientation() == wxLANDSCAPE ? "Landscape" : "Portrait");
|
||||
settings += wxString::Format("Paper Size: %s\n", GetPaperSizeName(m_printData->GetPaperId()));
|
||||
settings += wxString::Format("Quality: %d dpi\n", m_printData->GetQuality());
|
||||
settings += wxString::Format("Colour: %s", m_printData->GetColour() ? "Yes" : "No");
|
||||
|
||||
m_settingsText->SetValue(settings);
|
||||
}
|
||||
|
||||
wxString GetPaperSizeName(wxPaperSize paperId)
|
||||
{
|
||||
switch (paperId)
|
||||
{
|
||||
case wxPAPER_LETTER: return "Letter";
|
||||
case wxPAPER_A4: return "A4";
|
||||
case wxPAPER_A3: return "A3";
|
||||
case wxPAPER_LEGAL: return "Legal";
|
||||
default: return "Default";
|
||||
}
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxPanel* m_previewPanel;
|
||||
wxTextCtrl* m_settingsText;
|
||||
wxTextCtrl* m_log;
|
||||
wxPrintData* m_printData;
|
||||
wxPageSetupDialogData* m_pageSetupData;
|
||||
};
|
||||
|
||||
class PrintPreviewApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
PrintPreviewFrame* frame = new PrintPreviewFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(PrintPreviewApp);
|
||||
246
tests/apps/standalone/propgrid/propgrid_test.cpp
Normal file
246
tests/apps/standalone/propgrid/propgrid_test.cpp
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
// wxPropertyGrid Test - Tests property grid in WASM
|
||||
// KiCad uses property grids for property panels in ALL editors
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/notebook.h"
|
||||
#include "wx/propgrid/propgrid.h"
|
||||
#include "wx/propgrid/manager.h"
|
||||
#include "wx/propgrid/advprops.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class PropGridTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class PropGridTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
PropGridTestFrame();
|
||||
|
||||
private:
|
||||
wxPropertyGrid* m_propGrid;
|
||||
wxPropertyGridManager* m_propGridManager;
|
||||
wxTextCtrl* m_log;
|
||||
wxNotebook* m_notebook;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void PopulateBasicGrid();
|
||||
void PopulateManagerGrid();
|
||||
|
||||
void OnPropertyChanged(wxPropertyGridEvent& evt);
|
||||
void OnPropertyChanging(wxPropertyGridEvent& evt);
|
||||
void OnPropertySelected(wxPropertyGridEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_PROPGRID = wxID_HIGHEST + 1,
|
||||
ID_PROPGRID_MANAGER,
|
||||
ID_NOTEBOOK
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(PropGridTestFrame, wxFrame)
|
||||
EVT_PG_CHANGED(ID_PROPGRID, PropGridTestFrame::OnPropertyChanged)
|
||||
EVT_PG_CHANGING(ID_PROPGRID, PropGridTestFrame::OnPropertyChanging)
|
||||
EVT_PG_SELECTED(ID_PROPGRID, PropGridTestFrame::OnPropertySelected)
|
||||
EVT_PG_CHANGED(ID_PROPGRID_MANAGER, PropGridTestFrame::OnPropertyChanged)
|
||||
EVT_PG_SELECTED(ID_PROPGRID_MANAGER, PropGridTestFrame::OnPropertySelected)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(PropGridTestApp);
|
||||
|
||||
bool PropGridTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
PropGridTestFrame* frame = new PropGridTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
PropGridTestFrame::PropGridTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxPropertyGrid WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 700))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxPropertyGrid Test\n\n"
|
||||
"KiCad uses wxPropertyGrid for property panels in ALL editors.\n"
|
||||
"This tests property grid rendering and editing.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Notebook with two tabs
|
||||
m_notebook = new wxNotebook(this, ID_NOTEBOOK);
|
||||
|
||||
// Tab 1: Basic PropertyGrid
|
||||
wxPanel* basicPanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* basicSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_propGrid = new wxPropertyGrid(basicPanel, ID_PROPGRID,
|
||||
wxDefaultPosition, wxSize(-1, 300),
|
||||
wxPG_SPLITTER_AUTO_CENTER | wxPG_DEFAULT_STYLE);
|
||||
basicSizer->Add(m_propGrid, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
basicPanel->SetSizer(basicSizer);
|
||||
m_notebook->AddPage(basicPanel, "Basic PropertyGrid");
|
||||
|
||||
// Tab 2: PropertyGridManager (multi-page)
|
||||
wxPanel* managerPanel = new wxPanel(m_notebook);
|
||||
wxBoxSizer* managerSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_propGridManager = new wxPropertyGridManager(managerPanel, ID_PROPGRID_MANAGER,
|
||||
wxDefaultPosition, wxSize(-1, 300),
|
||||
wxPG_SPLITTER_AUTO_CENTER | wxPGMAN_DEFAULT_STYLE);
|
||||
managerSizer->Add(m_propGridManager, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
managerPanel->SetSizer(managerSizer);
|
||||
m_notebook->AddPage(managerPanel, "PropertyGridManager");
|
||||
|
||||
mainSizer->Add(m_notebook, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
// Populate grids after layout is set
|
||||
PopulateBasicGrid();
|
||||
PopulateManagerGrid();
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready - wxPropertyGrid test");
|
||||
|
||||
LogEvent("PropertyGrid test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PROPGRID_TEST] wxPropertyGrid test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void PropGridTestFrame::PopulateBasicGrid()
|
||||
{
|
||||
// KiCad-like properties for a component
|
||||
m_propGrid->Append(new wxPropertyCategory("General"));
|
||||
m_propGrid->Append(new wxStringProperty("Reference", wxPG_LABEL, "R1"));
|
||||
m_propGrid->Append(new wxStringProperty("Value", wxPG_LABEL, "10k"));
|
||||
m_propGrid->Append(new wxStringProperty("Footprint", wxPG_LABEL, "Resistor_SMD:R_0402"));
|
||||
|
||||
m_propGrid->Append(new wxPropertyCategory("Position"));
|
||||
m_propGrid->Append(new wxFloatProperty("X", wxPG_LABEL, 100.5));
|
||||
m_propGrid->Append(new wxFloatProperty("Y", wxPG_LABEL, 50.25));
|
||||
m_propGrid->Append(new wxIntProperty("Rotation", wxPG_LABEL, 90));
|
||||
|
||||
m_propGrid->Append(new wxPropertyCategory("Display"));
|
||||
m_propGrid->Append(new wxBoolProperty("Show Reference", wxPG_LABEL, true));
|
||||
m_propGrid->Append(new wxBoolProperty("Show Value", wxPG_LABEL, true));
|
||||
|
||||
// Color property (KiCad layer colors)
|
||||
m_propGrid->Append(new wxPropertyCategory("Colors"));
|
||||
m_propGrid->Append(new wxColourProperty("Front Copper", wxPG_LABEL, wxColour(255, 0, 0)));
|
||||
m_propGrid->Append(new wxColourProperty("Back Copper", wxPG_LABEL, wxColour(0, 0, 255)));
|
||||
|
||||
// Enum property (like KiCad layer selection)
|
||||
wxPGChoices layerChoices;
|
||||
layerChoices.Add("F.Cu", 0);
|
||||
layerChoices.Add("B.Cu", 1);
|
||||
layerChoices.Add("F.SilkS", 2);
|
||||
layerChoices.Add("B.SilkS", 3);
|
||||
layerChoices.Add("Edge.Cuts", 4);
|
||||
m_propGrid->Append(new wxEnumProperty("Layer", wxPG_LABEL, layerChoices, 0));
|
||||
|
||||
LogEvent("Basic PropertyGrid populated with KiCad-like properties");
|
||||
}
|
||||
|
||||
void PropGridTestFrame::PopulateManagerGrid()
|
||||
{
|
||||
// Page 1: Component properties
|
||||
wxPropertyGridPage* page1 = m_propGridManager->AddPage("Component");
|
||||
page1->Append(new wxPropertyCategory("Identity"));
|
||||
page1->Append(new wxStringProperty("Reference", wxPG_LABEL, "U1"));
|
||||
page1->Append(new wxStringProperty("Value", wxPG_LABEL, "STM32F103"));
|
||||
page1->Append(new wxStringProperty("Library", wxPG_LABEL, "MCU_ST_STM32F1"));
|
||||
|
||||
page1->Append(new wxPropertyCategory("Attributes"));
|
||||
page1->Append(new wxBoolProperty("Exclude from BOM", wxPG_LABEL, false));
|
||||
page1->Append(new wxBoolProperty("Exclude from Board", wxPG_LABEL, false));
|
||||
|
||||
// Page 2: Footprint properties
|
||||
wxPropertyGridPage* page2 = m_propGridManager->AddPage("Footprint");
|
||||
page2->Append(new wxPropertyCategory("Footprint"));
|
||||
page2->Append(new wxStringProperty("Name", wxPG_LABEL, "LQFP-48_7x7mm_P0.5mm"));
|
||||
page2->Append(new wxIntProperty("Pads", wxPG_LABEL, 48));
|
||||
|
||||
page2->Append(new wxPropertyCategory("3D Model"));
|
||||
page2->Append(new wxStringProperty("3D Model Path", wxPG_LABEL, "${KISYS3DMOD}/Package_QFP.3dshapes/LQFP-48_7x7mm_P0.5mm.wrl"));
|
||||
page2->Append(new wxFloatProperty("Scale X", wxPG_LABEL, 1.0));
|
||||
page2->Append(new wxFloatProperty("Scale Y", wxPG_LABEL, 1.0));
|
||||
page2->Append(new wxFloatProperty("Scale Z", wxPG_LABEL, 1.0));
|
||||
|
||||
// Page 3: Net properties
|
||||
wxPropertyGridPage* page3 = m_propGridManager->AddPage("Net");
|
||||
page3->Append(new wxPropertyCategory("Net Info"));
|
||||
page3->Append(new wxStringProperty("Net Name", wxPG_LABEL, "VCC"));
|
||||
page3->Append(new wxIntProperty("Net Code", wxPG_LABEL, 42));
|
||||
page3->Append(new wxIntProperty("Connected Pads", wxPG_LABEL, 12));
|
||||
|
||||
LogEvent("PropertyGridManager populated with 3 pages");
|
||||
}
|
||||
|
||||
void PropGridTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[PROPGRID_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void PropGridTestFrame::OnPropertyChanged(wxPropertyGridEvent& evt)
|
||||
{
|
||||
wxPGProperty* prop = evt.GetProperty();
|
||||
if (prop) {
|
||||
LogEvent(wxString::Format("Property changed: '%s' = '%s'",
|
||||
prop->GetName(), prop->GetValueAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
void PropGridTestFrame::OnPropertyChanging(wxPropertyGridEvent& evt)
|
||||
{
|
||||
wxPGProperty* prop = evt.GetProperty();
|
||||
if (prop) {
|
||||
LogEvent(wxString::Format("Property changing: '%s' -> '%s'",
|
||||
prop->GetName(), evt.GetValue().GetString()));
|
||||
}
|
||||
}
|
||||
|
||||
void PropGridTestFrame::OnPropertySelected(wxPropertyGridEvent& evt)
|
||||
{
|
||||
wxPGProperty* prop = evt.GetProperty();
|
||||
if (prop) {
|
||||
LogEvent(wxString::Format("Property selected: '%s'", prop->GetName()));
|
||||
}
|
||||
}
|
||||
235
tests/apps/standalone/regions/regions_test.cpp
Normal file
235
tests/apps/standalone/regions/regions_test.cpp
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// Region Clipping Test - Tests non-rectangular clipping regions
|
||||
// Tests: wxRegion, wxDC::SetDeviceClippingRegion(), Union/Subtract/Intersect
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/dcmemory.h"
|
||||
#include "wx/region.h"
|
||||
|
||||
class RegionsFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
RegionsFrame() : wxFrame(nullptr, wxID_ANY, "Region Clipping Test",
|
||||
wxDefaultPosition, wxSize(900, 700))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"Tests non-rectangular region clipping.\n"
|
||||
"Uses wxRegion with Union, Subtract, and Intersect operations.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Drawing panel
|
||||
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 550));
|
||||
m_drawPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_drawPanel->Bind(wxEVT_PAINT, &RegionsFrame::OnPaint, this);
|
||||
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Region clipping test app started");
|
||||
Log("Region clipping test app started");
|
||||
Log("Testing non-rectangular region clipping");
|
||||
}
|
||||
|
||||
private:
|
||||
void DrawTestPattern(wxDC& dc, int x, int y, int w, int h)
|
||||
{
|
||||
// Draw a gradient-like pattern that makes clipping visible
|
||||
for (int i = 0; i < w; i += 5)
|
||||
{
|
||||
dc.SetPen(wxPen(wxColour(255 * i / w, 100, 255 - 255 * i / w)));
|
||||
dc.DrawLine(x + i, y, x + i, y + h);
|
||||
}
|
||||
|
||||
// Draw some shapes
|
||||
dc.SetBrush(wxBrush(wxColour(255, 200, 0, 128)));
|
||||
dc.SetPen(wxPen(*wxBLACK, 2));
|
||||
dc.DrawCircle(x + w/2, y + h/2, w/3);
|
||||
|
||||
dc.SetBrush(wxBrush(wxColour(0, 200, 255, 128)));
|
||||
dc.DrawRectangle(x + w/4, y + h/4, w/2, h/2);
|
||||
}
|
||||
|
||||
void OnPaint(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_drawPanel);
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
int y = 10;
|
||||
const int boxWidth = 200;
|
||||
const int boxHeight = 150;
|
||||
const int spacing = 20;
|
||||
|
||||
dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
|
||||
|
||||
// Row 1: Basic region types
|
||||
|
||||
// 1a: No clipping (reference)
|
||||
dc.DrawText("No Clipping (reference)", 20, y);
|
||||
y += 15;
|
||||
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
|
||||
|
||||
// 1b: Single rectangle clipping
|
||||
dc.DrawText("Single Rectangle", 20 + boxWidth + spacing, y - 15);
|
||||
wxRegion singleRect(20 + boxWidth + spacing + 20, y + 20, boxWidth - 40, boxHeight - 40);
|
||||
dc.SetDeviceClippingRegion(singleRect);
|
||||
DrawTestPattern(dc, 20 + boxWidth + spacing, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 1c: Two rectangles (union)
|
||||
dc.DrawText("Two Rectangles (Union)", 20 + (boxWidth + spacing) * 2, y - 15);
|
||||
wxRegion twoRects;
|
||||
twoRects.Union(wxRect(20 + (boxWidth + spacing) * 2, y, boxWidth / 2, boxHeight / 2));
|
||||
twoRects.Union(wxRect(20 + (boxWidth + spacing) * 2 + boxWidth / 2, y + boxHeight / 2, boxWidth / 2, boxHeight / 2));
|
||||
dc.SetDeviceClippingRegion(twoRects);
|
||||
DrawTestPattern(dc, 20 + (boxWidth + spacing) * 2, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 1d: L-shaped region
|
||||
dc.DrawText("L-Shape (Union)", 20 + (boxWidth + spacing) * 3, y - 15);
|
||||
wxRegion lShape;
|
||||
int lx = 20 + (boxWidth + spacing) * 3;
|
||||
lShape.Union(wxRect(lx, y, boxWidth / 3, boxHeight)); // Vertical bar
|
||||
lShape.Union(wxRect(lx, y + boxHeight * 2 / 3, boxWidth, boxHeight / 3)); // Horizontal bar
|
||||
dc.SetDeviceClippingRegion(lShape);
|
||||
DrawTestPattern(dc, lx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
y += boxHeight + 40;
|
||||
|
||||
// Row 2: Complex regions
|
||||
|
||||
// 2a: Cross shape
|
||||
dc.DrawText("Cross (Two Rects)", 20, y);
|
||||
y += 15;
|
||||
wxRegion cross;
|
||||
cross.Union(wxRect(20 + boxWidth / 3, y, boxWidth / 3, boxHeight)); // Vertical
|
||||
cross.Union(wxRect(20, y + boxHeight / 3, boxWidth, boxHeight / 3)); // Horizontal
|
||||
dc.SetDeviceClippingRegion(cross);
|
||||
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 2b: Checkerboard pattern (4 squares)
|
||||
dc.DrawText("Checkerboard (4 Rects)", 20 + boxWidth + spacing, y - 15);
|
||||
wxRegion checkerboard;
|
||||
int cx = 20 + boxWidth + spacing;
|
||||
int halfW = boxWidth / 2;
|
||||
int halfH = boxHeight / 2;
|
||||
checkerboard.Union(wxRect(cx, y, halfW, halfH));
|
||||
checkerboard.Union(wxRect(cx + halfW, y + halfH, halfW, halfH));
|
||||
dc.SetDeviceClippingRegion(checkerboard);
|
||||
DrawTestPattern(dc, cx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 2c: Diagonal stripes (multiple rectangles)
|
||||
dc.DrawText("Stripes (Multiple)", 20 + (boxWidth + spacing) * 2, y - 15);
|
||||
wxRegion stripes;
|
||||
int sx = 20 + (boxWidth + spacing) * 2;
|
||||
int stripeW = boxWidth / 5;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
stripes.Union(wxRect(sx + i * stripeW * 2, y, stripeW, boxHeight));
|
||||
}
|
||||
dc.SetDeviceClippingRegion(stripes);
|
||||
DrawTestPattern(dc, sx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 2d: Frame (subtract inner rect)
|
||||
dc.DrawText("Frame (Subtract)", 20 + (boxWidth + spacing) * 3, y - 15);
|
||||
wxRegion frame(20 + (boxWidth + spacing) * 3, y, boxWidth, boxHeight);
|
||||
int fx = 20 + (boxWidth + spacing) * 3;
|
||||
frame.Subtract(wxRect(fx + 30, y + 30, boxWidth - 60, boxHeight - 60));
|
||||
dc.SetDeviceClippingRegion(frame);
|
||||
DrawTestPattern(dc, fx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
y += boxHeight + 40;
|
||||
|
||||
// Row 3: More complex patterns
|
||||
|
||||
// 3a: Grid pattern
|
||||
dc.DrawText("Grid (9 Rects)", 20, y);
|
||||
y += 15;
|
||||
wxRegion grid;
|
||||
int cellW = boxWidth / 5;
|
||||
int cellH = boxHeight / 5;
|
||||
for (int row = 0; row < 3; row++)
|
||||
{
|
||||
for (int col = 0; col < 3; col++)
|
||||
{
|
||||
grid.Union(wxRect(20 + col * cellW * 2, y + row * cellH * 2, cellW, cellH));
|
||||
}
|
||||
}
|
||||
dc.SetDeviceClippingRegion(grid);
|
||||
DrawTestPattern(dc, 20, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 3b: T-shape
|
||||
dc.DrawText("T-Shape", 20 + boxWidth + spacing, y - 15);
|
||||
wxRegion tShape;
|
||||
int tx = 20 + boxWidth + spacing;
|
||||
tShape.Union(wxRect(tx, y, boxWidth, boxHeight / 3)); // Top bar
|
||||
tShape.Union(wxRect(tx + boxWidth / 3, y, boxWidth / 3, boxHeight)); // Vertical stem
|
||||
dc.SetDeviceClippingRegion(tShape);
|
||||
DrawTestPattern(dc, tx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 3c: Corner rectangles
|
||||
dc.DrawText("4 Corners", 20 + (boxWidth + spacing) * 2, y - 15);
|
||||
wxRegion corners;
|
||||
int cornx = 20 + (boxWidth + spacing) * 2;
|
||||
int cornSize = boxWidth / 4;
|
||||
corners.Union(wxRect(cornx, y, cornSize, cornSize)); // Top-left
|
||||
corners.Union(wxRect(cornx + boxWidth - cornSize, y, cornSize, cornSize)); // Top-right
|
||||
corners.Union(wxRect(cornx, y + boxHeight - cornSize, cornSize, cornSize)); // Bottom-left
|
||||
corners.Union(wxRect(cornx + boxWidth - cornSize, y + boxHeight - cornSize, cornSize, cornSize)); // Bottom-right
|
||||
dc.SetDeviceClippingRegion(corners);
|
||||
DrawTestPattern(dc, cornx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
|
||||
// 3d: Staircase
|
||||
dc.DrawText("Staircase", 20 + (boxWidth + spacing) * 3, y - 15);
|
||||
wxRegion stairs;
|
||||
int stx = 20 + (boxWidth + spacing) * 3;
|
||||
int stepW = boxWidth / 4;
|
||||
int stepH = boxHeight / 4;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
stairs.Union(wxRect(stx + i * stepW, y + i * stepH, boxWidth - i * stepW, stepH));
|
||||
}
|
||||
dc.SetDeviceClippingRegion(stairs);
|
||||
DrawTestPattern(dc, stx, y, boxWidth, boxHeight);
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxPanel* m_drawPanel;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class RegionsApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
RegionsFrame* frame = new RegionsFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(RegionsApp);
|
||||
278
tests/apps/standalone/specialized/specialized_test.cpp
Normal file
278
tests/apps/standalone/specialized/specialized_test.cpp
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
// Specialized wxWidgets Controls Test
|
||||
// Tests: wxTreebook, wxBitmapComboBox, wxSpinCtrl
|
||||
// These are used in KiCad for settings dialogs and layer management
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/treebook.h"
|
||||
#include "wx/bmpcbox.h"
|
||||
#include "wx/dcmemory.h"
|
||||
#include "wx/spinctrl.h"
|
||||
|
||||
// Helper to create color swatch bitmaps for wxBitmapComboBox
|
||||
wxBitmap CreateColorSwatch(const wxColour& color, int width = 16, int height = 16)
|
||||
{
|
||||
wxBitmap bmp(width, height);
|
||||
wxMemoryDC dc(bmp);
|
||||
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(wxBrush(color));
|
||||
dc.DrawRectangle(0, 0, width, height);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
class SpecializedFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
SpecializedFrame() : wxFrame(nullptr, wxID_ANY, "Specialized wxWidgets Controls Test",
|
||||
wxDefaultPosition, wxSize(900, 700))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses specialized controls for settings and layer management.\n"
|
||||
"Tests: wxTreebook (settings pages), wxBitmapComboBox (layer chooser), wxListBox (layer list).");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Split into left and right panels
|
||||
wxBoxSizer* contentSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Left: wxTreebook
|
||||
wxStaticBoxSizer* treebookSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "wxTreebook (Settings)");
|
||||
CreateTreebook(mainPanel);
|
||||
treebookSizer->Add(m_treebook, 1, wxEXPAND | wxALL, 5);
|
||||
contentSizer->Add(treebookSizer, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Right: wxBitmapComboBox and wxListBox for layer list
|
||||
wxBoxSizer* rightSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// wxBitmapComboBox
|
||||
wxStaticBoxSizer* bmpComboSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "wxBitmapComboBox (Layer Chooser)");
|
||||
CreateBitmapComboBox(mainPanel);
|
||||
bmpComboSizer->Add(m_layerCombo, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxStaticText* comboLabel = new wxStaticText(mainPanel, wxID_ANY, "Selected: (none)");
|
||||
m_comboLabel = comboLabel;
|
||||
bmpComboSizer->Add(comboLabel, 0, wxALL, 5);
|
||||
|
||||
rightSizer->Add(bmpComboSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Layer List (simulating wxRearrangeCtrl with wxCheckListBox)
|
||||
wxStaticBoxSizer* listSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Layer List (Visibility)");
|
||||
CreateLayerList(mainPanel);
|
||||
listSizer->Add(m_layerList, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxButton* btnGetOrder = new wxButton(mainPanel, wxID_ANY, "Get Layer Status");
|
||||
btnGetOrder->Bind(wxEVT_BUTTON, &SpecializedFrame::OnGetOrder, this);
|
||||
listSizer->Add(btnGetOrder, 0, wxALL, 5);
|
||||
|
||||
rightSizer->Add(listSizer, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
contentSizer->Add(rightSizer, 1, wxEXPAND);
|
||||
|
||||
mainSizer->Add(contentSizer, 1, wxEXPAND);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Specialized controls test app started");
|
||||
Log("Specialized controls test app started");
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateTreebook(wxWindow* parent)
|
||||
{
|
||||
m_treebook = new wxTreebook(parent, wxID_ANY);
|
||||
|
||||
// General settings page
|
||||
wxPanel* generalPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* generalSizer = new wxBoxSizer(wxVERTICAL);
|
||||
generalSizer->Add(new wxStaticText(generalPage, wxID_ANY, "General Settings"), 0, wxALL, 10);
|
||||
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Show grid"), 0, wxALL, 5);
|
||||
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Auto-save"), 0, wxALL, 5);
|
||||
generalSizer->Add(new wxCheckBox(generalPage, wxID_ANY, "Show welcome dialog"), 0, wxALL, 5);
|
||||
generalPage->SetSizer(generalSizer);
|
||||
m_treebook->AddPage(generalPage, "General");
|
||||
|
||||
// Display page with sub-pages
|
||||
wxPanel* displayPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* displaySizer = new wxBoxSizer(wxVERTICAL);
|
||||
displaySizer->Add(new wxStaticText(displayPage, wxID_ANY, "Display Settings"), 0, wxALL, 10);
|
||||
displaySizer->Add(new wxCheckBox(displayPage, wxID_ANY, "Anti-aliasing"), 0, wxALL, 5);
|
||||
displayPage->SetSizer(displaySizer);
|
||||
m_treebook->AddPage(displayPage, "Display");
|
||||
|
||||
// Display sub-page: Colors
|
||||
wxPanel* colorsPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* colorsSizer = new wxBoxSizer(wxVERTICAL);
|
||||
colorsSizer->Add(new wxStaticText(colorsPage, wxID_ANY, "Color Settings"), 0, wxALL, 10);
|
||||
colorsSizer->Add(new wxStaticText(colorsPage, wxID_ANY, "Background:"), 0, wxLEFT | wxTOP, 5);
|
||||
wxChoice* bgChoice = new wxChoice(colorsPage, wxID_ANY);
|
||||
bgChoice->Append("White");
|
||||
bgChoice->Append("Black");
|
||||
bgChoice->Append("Gray");
|
||||
bgChoice->SetSelection(0);
|
||||
colorsSizer->Add(bgChoice, 0, wxALL, 5);
|
||||
colorsPage->SetSizer(colorsSizer);
|
||||
m_treebook->AddSubPage(colorsPage, "Colors");
|
||||
|
||||
// Display sub-page: Grid
|
||||
wxPanel* gridPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* gridSizer = new wxBoxSizer(wxVERTICAL);
|
||||
gridSizer->Add(new wxStaticText(gridPage, wxID_ANY, "Grid Settings"), 0, wxALL, 10);
|
||||
gridSizer->Add(new wxStaticText(gridPage, wxID_ANY, "Grid size (mm):"), 0, wxLEFT | wxTOP, 5);
|
||||
gridSizer->Add(new wxSpinCtrl(gridPage, wxID_ANY, "1", wxDefaultPosition, wxDefaultSize,
|
||||
wxSP_ARROW_KEYS, 1, 100, 1), 0, wxALL, 5);
|
||||
gridPage->SetSizer(gridSizer);
|
||||
m_treebook->AddSubPage(gridPage, "Grid");
|
||||
|
||||
// Editing page
|
||||
wxPanel* editPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* editSizer = new wxBoxSizer(wxVERTICAL);
|
||||
editSizer->Add(new wxStaticText(editPage, wxID_ANY, "Editing Settings"), 0, wxALL, 10);
|
||||
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Magnetic pads"), 0, wxALL, 5);
|
||||
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Magnetic graphics"), 0, wxALL, 5);
|
||||
editSizer->Add(new wxCheckBox(editPage, wxID_ANY, "Allow free pads"), 0, wxALL, 5);
|
||||
editPage->SetSizer(editSizer);
|
||||
m_treebook->AddPage(editPage, "Editing");
|
||||
|
||||
// Editing sub-page: Defaults
|
||||
wxPanel* defaultsPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* defaultsSizer = new wxBoxSizer(wxVERTICAL);
|
||||
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Default Values"), 0, wxALL, 10);
|
||||
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Track width (mm):"), 0, wxLEFT | wxTOP, 5);
|
||||
defaultsSizer->Add(new wxTextCtrl(defaultsPage, wxID_ANY, "0.25"), 0, wxALL, 5);
|
||||
defaultsSizer->Add(new wxStaticText(defaultsPage, wxID_ANY, "Via size (mm):"), 0, wxLEFT | wxTOP, 5);
|
||||
defaultsSizer->Add(new wxTextCtrl(defaultsPage, wxID_ANY, "0.8"), 0, wxALL, 5);
|
||||
defaultsPage->SetSizer(defaultsSizer);
|
||||
m_treebook->AddSubPage(defaultsPage, "Defaults");
|
||||
|
||||
// Printing page
|
||||
wxPanel* printPage = new wxPanel(m_treebook);
|
||||
wxBoxSizer* printSizer = new wxBoxSizer(wxVERTICAL);
|
||||
printSizer->Add(new wxStaticText(printPage, wxID_ANY, "Print Settings"), 0, wxALL, 10);
|
||||
printSizer->Add(new wxCheckBox(printPage, wxID_ANY, "Print mirrored"), 0, wxALL, 5);
|
||||
printSizer->Add(new wxCheckBox(printPage, wxID_ANY, "Print in black"), 0, wxALL, 5);
|
||||
printPage->SetSizer(printSizer);
|
||||
m_treebook->AddPage(printPage, "Printing");
|
||||
|
||||
m_treebook->Bind(wxEVT_TREEBOOK_PAGE_CHANGED, &SpecializedFrame::OnTreebookPageChanged, this);
|
||||
}
|
||||
|
||||
void CreateBitmapComboBox(wxWindow* parent)
|
||||
{
|
||||
m_layerCombo = new wxBitmapComboBox(parent, wxID_ANY, "", wxDefaultPosition,
|
||||
wxSize(200, -1), 0, nullptr, wxCB_READONLY);
|
||||
|
||||
// Add layers with color swatches
|
||||
m_layerCombo->Append("F.Cu (Top Copper)", CreateColorSwatch(*wxRED));
|
||||
m_layerCombo->Append("B.Cu (Bottom Copper)", CreateColorSwatch(*wxBLUE));
|
||||
m_layerCombo->Append("F.SilkS (Top Silk)", CreateColorSwatch(*wxYELLOW));
|
||||
m_layerCombo->Append("B.SilkS (Bottom Silk)", CreateColorSwatch(wxColour(255, 0, 255)));
|
||||
m_layerCombo->Append("F.Mask (Top Mask)", CreateColorSwatch(wxColour(0, 128, 0)));
|
||||
m_layerCombo->Append("B.Mask (Bottom Mask)", CreateColorSwatch(wxColour(0, 128, 128)));
|
||||
m_layerCombo->Append("Edge.Cuts", CreateColorSwatch(*wxWHITE));
|
||||
m_layerCombo->Append("Dwgs.User", CreateColorSwatch(wxColour(128, 128, 128)));
|
||||
|
||||
m_layerCombo->SetSelection(0);
|
||||
|
||||
m_layerCombo->Bind(wxEVT_COMBOBOX, &SpecializedFrame::OnLayerComboChanged, this);
|
||||
}
|
||||
|
||||
void CreateLayerList(wxWindow* parent)
|
||||
{
|
||||
wxArrayString items;
|
||||
items.Add("F.Cu");
|
||||
items.Add("In1.Cu");
|
||||
items.Add("In2.Cu");
|
||||
items.Add("B.Cu");
|
||||
items.Add("F.SilkS");
|
||||
items.Add("B.SilkS");
|
||||
items.Add("F.Mask");
|
||||
items.Add("B.Mask");
|
||||
|
||||
m_layerList = new wxCheckListBox(parent, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(-1, 200), items);
|
||||
|
||||
// Check all by default
|
||||
for (unsigned int i = 0; i < m_layerList->GetCount(); i++)
|
||||
{
|
||||
m_layerList->Check(i, true);
|
||||
}
|
||||
|
||||
m_layerList->Bind(wxEVT_CHECKLISTBOX, &SpecializedFrame::OnLayerListCheck, this);
|
||||
}
|
||||
|
||||
void OnTreebookPageChanged(wxBookCtrlEvent& event)
|
||||
{
|
||||
int page = event.GetSelection();
|
||||
wxString pageName = m_treebook->GetPageText(page);
|
||||
Log(wxString::Format("Treebook page changed to: %s (page %d)", pageName, page));
|
||||
}
|
||||
|
||||
void OnLayerComboChanged(wxCommandEvent& event)
|
||||
{
|
||||
int sel = m_layerCombo->GetSelection();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
wxString layer = m_layerCombo->GetString(sel);
|
||||
m_comboLabel->SetLabel(wxString::Format("Selected: %s", layer));
|
||||
Log(wxString::Format("Layer selected: %s", layer));
|
||||
}
|
||||
}
|
||||
|
||||
void OnLayerListCheck(wxCommandEvent& event)
|
||||
{
|
||||
int idx = event.GetInt();
|
||||
bool checked = m_layerList->IsChecked(idx);
|
||||
wxString layer = m_layerList->GetString(idx);
|
||||
Log(wxString::Format("Layer %s visibility: %s", layer, checked ? "visible" : "hidden"));
|
||||
}
|
||||
|
||||
void OnGetOrder(wxCommandEvent& event)
|
||||
{
|
||||
wxString orderStr = "Layer status:\n";
|
||||
|
||||
for (unsigned int i = 0; i < m_layerList->GetCount(); i++)
|
||||
{
|
||||
wxString layer = m_layerList->GetString(i);
|
||||
bool visible = m_layerList->IsChecked(i);
|
||||
orderStr += wxString::Format(" %d. %s [%s]\n",
|
||||
(int)(i + 1), layer, visible ? "visible" : "hidden");
|
||||
}
|
||||
|
||||
Log(orderStr);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxTreebook* m_treebook;
|
||||
wxBitmapComboBox* m_layerCombo;
|
||||
wxStaticText* m_comboLabel;
|
||||
wxCheckListBox* m_layerList;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class SpecializedApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
SpecializedFrame* frame = new SpecializedFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(SpecializedApp);
|
||||
405
tests/apps/standalone/stc/stc_test.cpp
Normal file
405
tests/apps/standalone/stc/stc_test.cpp
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
// wxStyledTextCtrl Test - Tests Scintilla-based text editor in WASM
|
||||
// KiCad uses wxStyledTextCtrl for:
|
||||
// - DRC rules editor
|
||||
// - Python console
|
||||
// - Custom script editors
|
||||
// This is MEDIUM priority for KiCad functionality
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/stc/stc.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class StcTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class StcTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
StcTestFrame();
|
||||
|
||||
private:
|
||||
wxStyledTextCtrl* m_stc;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void SetupPythonLexer();
|
||||
void SetupDrcLexer();
|
||||
void SetupPlainText();
|
||||
|
||||
// Event handlers
|
||||
void OnPythonMode(wxCommandEvent& evt);
|
||||
void OnDrcMode(wxCommandEvent& evt);
|
||||
void OnPlainMode(wxCommandEvent& evt);
|
||||
void OnInsertSample(wxCommandEvent& evt);
|
||||
void OnClearText(wxCommandEvent& evt);
|
||||
void OnShowLineNumbers(wxCommandEvent& evt);
|
||||
void OnFoldCode(wxCommandEvent& evt);
|
||||
|
||||
// STC events
|
||||
void OnStcChange(wxStyledTextEvent& evt);
|
||||
void OnStcCharAdded(wxStyledTextEvent& evt);
|
||||
void OnStcMarginClick(wxStyledTextEvent& evt);
|
||||
void OnStcUpdateUI(wxStyledTextEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_STC = wxID_HIGHEST + 1,
|
||||
ID_PYTHON_MODE,
|
||||
ID_DRC_MODE,
|
||||
ID_PLAIN_MODE,
|
||||
ID_INSERT_SAMPLE,
|
||||
ID_CLEAR_TEXT,
|
||||
ID_SHOW_LINENUMS,
|
||||
ID_FOLD_CODE
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(StcTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_PYTHON_MODE, StcTestFrame::OnPythonMode)
|
||||
EVT_BUTTON(ID_DRC_MODE, StcTestFrame::OnDrcMode)
|
||||
EVT_BUTTON(ID_PLAIN_MODE, StcTestFrame::OnPlainMode)
|
||||
EVT_BUTTON(ID_INSERT_SAMPLE, StcTestFrame::OnInsertSample)
|
||||
EVT_BUTTON(ID_CLEAR_TEXT, StcTestFrame::OnClearText)
|
||||
EVT_BUTTON(ID_SHOW_LINENUMS, StcTestFrame::OnShowLineNumbers)
|
||||
EVT_BUTTON(ID_FOLD_CODE, StcTestFrame::OnFoldCode)
|
||||
EVT_STC_CHANGE(ID_STC, StcTestFrame::OnStcChange)
|
||||
EVT_STC_CHARADDED(ID_STC, StcTestFrame::OnStcCharAdded)
|
||||
EVT_STC_MARGINCLICK(ID_STC, StcTestFrame::OnStcMarginClick)
|
||||
EVT_STC_UPDATEUI(ID_STC, StcTestFrame::OnStcUpdateUI)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(StcTestApp);
|
||||
|
||||
bool StcTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
StcTestFrame* frame = new StcTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
StcTestFrame::StcTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxStyledTextCtrl WASM Test",
|
||||
wxDefaultPosition, wxSize(800, 700))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxStyledTextCtrl Test\n\n"
|
||||
"KiCad uses wxSTC for DRC rules editor, Python console, and script editors.\n"
|
||||
"Test syntax highlighting, line numbers, folding, and basic editing.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Button bar
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_PYTHON_MODE, "Python"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_DRC_MODE, "DRC Rules"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_PLAIN_MODE, "Plain"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_INSERT_SAMPLE, "Insert Sample"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_CLEAR_TEXT, "Clear"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_SHOW_LINENUMS, "Line Numbers"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_FOLD_CODE, "Fold All"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// wxStyledTextCtrl
|
||||
m_stc = new wxStyledTextCtrl(this, ID_STC, wxDefaultPosition, wxSize(-1, 350));
|
||||
|
||||
// Basic styling
|
||||
wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
m_stc->StyleSetFont(wxSTC_STYLE_DEFAULT, font);
|
||||
m_stc->StyleClearAll();
|
||||
|
||||
// Line numbers margin
|
||||
m_stc->SetMarginType(0, wxSTC_MARGIN_NUMBER);
|
||||
m_stc->SetMarginWidth(0, 40);
|
||||
|
||||
// Folding margin
|
||||
m_stc->SetMarginType(1, wxSTC_MARGIN_SYMBOL);
|
||||
m_stc->SetMarginMask(1, wxSTC_MASK_FOLDERS);
|
||||
m_stc->SetMarginWidth(1, 16);
|
||||
m_stc->SetMarginSensitive(1, true);
|
||||
|
||||
// Folding markers
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE);
|
||||
m_stc->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER);
|
||||
|
||||
// Enable folding
|
||||
m_stc->SetProperty("fold", "1");
|
||||
m_stc->SetFoldFlags(wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED);
|
||||
|
||||
mainSizer->Add(m_stc, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 100), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
// Set initial Python mode with sample content
|
||||
SetupPythonLexer();
|
||||
m_stc->SetText(
|
||||
"# KiCad Python Console Example\n"
|
||||
"import pcbnew\n"
|
||||
"\n"
|
||||
"def list_footprints():\n"
|
||||
" '''List all footprints on the board'''\n"
|
||||
" board = pcbnew.GetBoard()\n"
|
||||
" for fp in board.GetFootprints():\n"
|
||||
" print(f\"Footprint: {fp.GetReference()}\")\n"
|
||||
" print(f\" Value: {fp.GetValue()}\")\n"
|
||||
" print(f\" Position: {fp.GetPosition()}\")\n"
|
||||
"\n"
|
||||
"# Call the function\n"
|
||||
"list_footprints()\n"
|
||||
);
|
||||
|
||||
LogEvent("wxStyledTextCtrl test app started");
|
||||
LogEvent("Python mode enabled with sample code");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[STC_TEST] wxStyledTextCtrl test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void StcTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[STC_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void StcTestFrame::SetupPythonLexer()
|
||||
{
|
||||
m_stc->SetLexer(wxSTC_LEX_PYTHON);
|
||||
|
||||
// Python keywords
|
||||
m_stc->SetKeyWords(0, "and as assert async await break class continue def del elif else "
|
||||
"except finally for from global if import in is lambda nonlocal not "
|
||||
"or pass raise return try while with yield True False None");
|
||||
|
||||
// Styling for Python
|
||||
m_stc->StyleSetForeground(wxSTC_P_DEFAULT, *wxBLACK);
|
||||
m_stc->StyleSetForeground(wxSTC_P_COMMENTLINE, wxColour(0, 128, 0)); // Green
|
||||
m_stc->StyleSetForeground(wxSTC_P_NUMBER, wxColour(128, 0, 128)); // Purple
|
||||
m_stc->StyleSetForeground(wxSTC_P_STRING, wxColour(0, 0, 128)); // Blue
|
||||
m_stc->StyleSetForeground(wxSTC_P_CHARACTER, wxColour(0, 0, 128)); // Blue
|
||||
m_stc->StyleSetForeground(wxSTC_P_WORD, wxColour(0, 0, 255)); // Bright blue
|
||||
m_stc->StyleSetBold(wxSTC_P_WORD, true);
|
||||
m_stc->StyleSetForeground(wxSTC_P_TRIPLE, wxColour(127, 0, 0)); // Dark red
|
||||
m_stc->StyleSetForeground(wxSTC_P_TRIPLEDOUBLE, wxColour(127, 0, 0)); // Dark red
|
||||
m_stc->StyleSetForeground(wxSTC_P_CLASSNAME, wxColour(0, 128, 128)); // Teal
|
||||
m_stc->StyleSetBold(wxSTC_P_CLASSNAME, true);
|
||||
m_stc->StyleSetForeground(wxSTC_P_DEFNAME, wxColour(0, 128, 128)); // Teal
|
||||
m_stc->StyleSetBold(wxSTC_P_DEFNAME, true);
|
||||
m_stc->StyleSetForeground(wxSTC_P_OPERATOR, *wxBLACK);
|
||||
m_stc->StyleSetForeground(wxSTC_P_IDENTIFIER, *wxBLACK);
|
||||
m_stc->StyleSetForeground(wxSTC_P_DECORATOR, wxColour(255, 128, 0)); // Orange
|
||||
|
||||
// Enable Python-specific folding
|
||||
m_stc->SetProperty("fold.compact", "0");
|
||||
|
||||
m_stc->Colourise(0, -1);
|
||||
LogEvent("Python lexer configured");
|
||||
}
|
||||
|
||||
void StcTestFrame::SetupDrcLexer()
|
||||
{
|
||||
// DRC rules are similar to S-expressions - use Lisp lexer
|
||||
m_stc->SetLexer(wxSTC_LEX_LISP);
|
||||
|
||||
// Keywords for DRC rules
|
||||
m_stc->SetKeyWords(0, "version rule condition constraint layer net type "
|
||||
"min max opt clearance track_width via_diameter "
|
||||
"hole_size annular_width silk_clearance courtyward_clearance");
|
||||
|
||||
// Styling for DRC (Lisp-like)
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_DEFAULT, *wxBLACK);
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_COMMENT, wxColour(0, 128, 0)); // Green
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_NUMBER, wxColour(128, 0, 128)); // Purple
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_KEYWORD, wxColour(0, 0, 255)); // Bright blue
|
||||
m_stc->StyleSetBold(wxSTC_LISP_KEYWORD, true);
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_STRING, wxColour(0, 0, 128)); // Blue
|
||||
m_stc->StyleSetForeground(wxSTC_LISP_OPERATOR, wxColour(128, 0, 0)); // Red
|
||||
|
||||
m_stc->Colourise(0, -1);
|
||||
LogEvent("DRC rules lexer configured");
|
||||
}
|
||||
|
||||
void StcTestFrame::SetupPlainText()
|
||||
{
|
||||
m_stc->SetLexer(wxSTC_LEX_NULL);
|
||||
m_stc->StyleSetForeground(wxSTC_STYLE_DEFAULT, *wxBLACK);
|
||||
m_stc->StyleSetBackground(wxSTC_STYLE_DEFAULT, *wxWHITE);
|
||||
m_stc->StyleClearAll();
|
||||
LogEvent("Plain text mode enabled");
|
||||
}
|
||||
|
||||
void StcTestFrame::OnPythonMode(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetupPythonLexer();
|
||||
if (m_stc->GetTextLength() == 0) {
|
||||
m_stc->SetText(
|
||||
"# Python code here\n"
|
||||
"import pcbnew\n"
|
||||
"\n"
|
||||
"board = pcbnew.GetBoard()\n"
|
||||
"print(board)\n"
|
||||
);
|
||||
} else {
|
||||
m_stc->Colourise(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void StcTestFrame::OnDrcMode(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetupDrcLexer();
|
||||
if (m_stc->GetTextLength() == 0) {
|
||||
m_stc->SetText(
|
||||
"; KiCad DRC Rules Example\n"
|
||||
"(version 1)\n"
|
||||
"\n"
|
||||
"(rule \"Minimum track width\"\n"
|
||||
" (condition \"A.Type == 'track'\")\n"
|
||||
" (constraint track_width (min 0.2mm)))\n"
|
||||
"\n"
|
||||
"(rule \"Via size\"\n"
|
||||
" (condition \"A.Type == 'via'\")\n"
|
||||
" (constraint via_diameter (min 0.6mm))\n"
|
||||
" (constraint hole_size (min 0.3mm)))\n"
|
||||
);
|
||||
} else {
|
||||
m_stc->Colourise(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void StcTestFrame::OnPlainMode(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
SetupPlainText();
|
||||
}
|
||||
|
||||
void StcTestFrame::OnInsertSample(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
static int sampleNum = 1;
|
||||
wxString sample = wxString::Format("\n# Sample insertion %d\nx = %d\nprint(x)\n", sampleNum, sampleNum);
|
||||
m_stc->AppendText(sample);
|
||||
LogEvent(wxString::Format("Inserted sample code #%d", sampleNum));
|
||||
sampleNum++;
|
||||
}
|
||||
|
||||
void StcTestFrame::OnClearText(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_stc->ClearAll();
|
||||
LogEvent("Text cleared");
|
||||
}
|
||||
|
||||
void StcTestFrame::OnShowLineNumbers(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
// Toggle line numbers
|
||||
if (m_stc->GetMarginWidth(0) > 0) {
|
||||
m_stc->SetMarginWidth(0, 0);
|
||||
LogEvent("Line numbers hidden");
|
||||
} else {
|
||||
m_stc->SetMarginWidth(0, 40);
|
||||
LogEvent("Line numbers shown");
|
||||
}
|
||||
}
|
||||
|
||||
void StcTestFrame::OnFoldCode(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
// Fold all
|
||||
for (int line = 0; line < m_stc->GetLineCount(); line++) {
|
||||
int level = m_stc->GetFoldLevel(line);
|
||||
if (level & wxSTC_FOLDLEVELHEADERFLAG) {
|
||||
if (m_stc->GetFoldExpanded(line)) {
|
||||
m_stc->ToggleFold(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
LogEvent("All code folded");
|
||||
}
|
||||
|
||||
void StcTestFrame::OnStcChange(wxStyledTextEvent& evt)
|
||||
{
|
||||
// Don't log every character - too noisy
|
||||
// Only log significant changes
|
||||
static int changeCount = 0;
|
||||
changeCount++;
|
||||
if (changeCount % 10 == 0) {
|
||||
LogEvent(wxString::Format("Text changed (%d modifications)", changeCount));
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void StcTestFrame::OnStcCharAdded(wxStyledTextEvent& evt)
|
||||
{
|
||||
int ch = evt.GetKey();
|
||||
if (ch == '\n') {
|
||||
// Auto-indent after newline
|
||||
int currentLine = m_stc->GetCurrentLine();
|
||||
if (currentLine > 0) {
|
||||
int prevLineIndent = m_stc->GetLineIndentation(currentLine - 1);
|
||||
m_stc->SetLineIndentation(currentLine, prevLineIndent);
|
||||
m_stc->GotoPos(m_stc->GetLineIndentPosition(currentLine));
|
||||
}
|
||||
LogEvent("Auto-indent applied");
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void StcTestFrame::OnStcMarginClick(wxStyledTextEvent& evt)
|
||||
{
|
||||
int line = m_stc->LineFromPosition(evt.GetPosition());
|
||||
int margin = evt.GetMargin();
|
||||
|
||||
if (margin == 1) { // Folding margin
|
||||
int level = m_stc->GetFoldLevel(line);
|
||||
if (level & wxSTC_FOLDLEVELHEADERFLAG) {
|
||||
m_stc->ToggleFold(line);
|
||||
LogEvent(wxString::Format("Toggled fold at line %d", line + 1));
|
||||
}
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void StcTestFrame::OnStcUpdateUI(wxStyledTextEvent& evt)
|
||||
{
|
||||
// Update status bar with cursor position
|
||||
int pos = m_stc->GetCurrentPos();
|
||||
int line = m_stc->GetCurrentLine();
|
||||
int col = m_stc->GetColumn(pos);
|
||||
SetStatusText(wxString::Format("Line %d, Col %d", line + 1, col + 1));
|
||||
evt.Skip();
|
||||
}
|
||||
151
tests/apps/standalone/textdecor/textdecor_test.cpp
Normal file
151
tests/apps/standalone/textdecor/textdecor_test.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// Text Decorations Test - Tests underline and strikethrough rendering
|
||||
// Tests: wxFont::SetUnderlined(), wxFont::SetStrikethrough(), wxDC::DrawText()
|
||||
|
||||
#include "wx/wx.h"
|
||||
|
||||
class TextDecorFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
TextDecorFrame() : wxFrame(nullptr, wxID_ANY, "Text Decorations Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"Tests underline and strikethrough text rendering.\n"
|
||||
"Uses wxFont::SetUnderlined() and wxFont::SetStrikethrough().");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Drawing panel
|
||||
m_drawPanel = new wxPanel(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(-1, 400));
|
||||
m_drawPanel->SetBackgroundColour(*wxWHITE);
|
||||
m_drawPanel->Bind(wxEVT_PAINT, &TextDecorFrame::OnPaint, this);
|
||||
mainSizer->Add(m_drawPanel, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Text decoration test app started");
|
||||
Log("Text decoration test app started");
|
||||
Log("Testing underline, strikethrough, and combined decorations");
|
||||
}
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event)
|
||||
{
|
||||
wxPaintDC dc(m_drawPanel);
|
||||
dc.SetBackground(*wxWHITE_BRUSH);
|
||||
dc.Clear();
|
||||
|
||||
int y = 20;
|
||||
const int lineHeight = 40;
|
||||
const int x = 20;
|
||||
|
||||
// Section 1: Normal text (no decorations)
|
||||
dc.SetFont(wxFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
dc.DrawText("Normal text (no decorations)", x, y);
|
||||
y += lineHeight;
|
||||
|
||||
// Section 2: Underlined text
|
||||
wxFont underlineFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
underlineFont.SetUnderlined(true);
|
||||
dc.SetFont(underlineFont);
|
||||
dc.DrawText("Underlined text", x, y);
|
||||
y += lineHeight;
|
||||
|
||||
// Section 3: Strikethrough text
|
||||
wxFont strikeFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
strikeFont.SetStrikethrough(true);
|
||||
dc.SetFont(strikeFont);
|
||||
dc.DrawText("Strikethrough text", x, y);
|
||||
y += lineHeight;
|
||||
|
||||
// Section 4: Both underline and strikethrough
|
||||
wxFont bothFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
bothFont.SetUnderlined(true);
|
||||
bothFont.SetStrikethrough(true);
|
||||
dc.SetFont(bothFont);
|
||||
dc.DrawText("Both underline and strikethrough", x, y);
|
||||
y += lineHeight + 10;
|
||||
|
||||
// Section 5: Different font sizes with underline
|
||||
dc.SetTextForeground(wxColour(0, 0, 128)); // Dark blue
|
||||
int sizes[] = {10, 14, 18, 24, 32};
|
||||
for (int size : sizes)
|
||||
{
|
||||
wxFont sizedFont(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
sizedFont.SetUnderlined(true);
|
||||
dc.SetFont(sizedFont);
|
||||
dc.DrawText(wxString::Format("%dpt underlined", size), x, y);
|
||||
y += size + 12;
|
||||
}
|
||||
|
||||
y += 10;
|
||||
|
||||
// Section 6: Different colors with strikethrough
|
||||
wxColour colors[] = {*wxRED, *wxGREEN, *wxBLUE, wxColour(128, 0, 128)};
|
||||
const char* colorNames[] = {"Red", "Green", "Blue", "Purple"};
|
||||
wxFont colorFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
colorFont.SetStrikethrough(true);
|
||||
dc.SetFont(colorFont);
|
||||
|
||||
int xPos = x;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
dc.SetTextForeground(colors[i]);
|
||||
dc.DrawText(wxString::Format("%s strikethrough", colorNames[i]), xPos, y);
|
||||
xPos += 180;
|
||||
}
|
||||
y += lineHeight;
|
||||
|
||||
// Section 7: Bold and italic with decorations
|
||||
y += 10;
|
||||
dc.SetTextForeground(*wxBLACK);
|
||||
|
||||
wxFont boldUnderline(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
|
||||
boldUnderline.SetUnderlined(true);
|
||||
dc.SetFont(boldUnderline);
|
||||
dc.DrawText("Bold + Underlined", x, y);
|
||||
|
||||
wxFont italicStrike(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL);
|
||||
italicStrike.SetStrikethrough(true);
|
||||
dc.SetFont(italicStrike);
|
||||
dc.DrawText("Italic + Strikethrough", x + 250, y);
|
||||
|
||||
wxFont boldItalicBoth(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD);
|
||||
boldItalicBoth.SetUnderlined(true);
|
||||
boldItalicBoth.SetStrikethrough(true);
|
||||
dc.SetFont(boldItalicBoth);
|
||||
dc.DrawText("Bold + Italic + Both", x + 500, y);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxPanel* m_drawPanel;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class TextDecorApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
TextDecorFrame* frame = new TextDecorFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(TextDecorApp);
|
||||
146
tests/apps/standalone/threadpool/threadpool_test.cpp
Normal file
146
tests/apps/standalone/threadpool/threadpool_test.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Thread Pool Deadlock Test
|
||||
*
|
||||
* This test replicates KiCad's pattern of creating hardware_concurrency() threads.
|
||||
* When PTHREAD_POOL_SIZE < hardware_concurrency(), this should deadlock because:
|
||||
* 1. Threads 1-N use pre-warmed Web Workers
|
||||
* 2. Thread N+1 needs new Web Worker (posts to event loop)
|
||||
* 3. Main thread busy-waits for thread to start
|
||||
* 4. Busy-wait blocks event loop -> Worker message never processed
|
||||
* 5. DEADLOCK
|
||||
*/
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class ThreadPoolFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
ThreadPoolFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "Thread Pool Deadlock Test",
|
||||
wxDefaultPosition, wxSize(800, 600))
|
||||
{
|
||||
// Log hardware_concurrency - this is what KiCad uses to determine thread count
|
||||
int num_threads = std::thread::hardware_concurrency();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] hardware_concurrency: ' + $0);
|
||||
}, num_threads);
|
||||
#endif
|
||||
printf("[THREADPOOL] hardware_concurrency: %d\n", num_threads);
|
||||
|
||||
// Replicate KiCad's exact pattern: create hardware_concurrency() threads
|
||||
// in the constructor (like BS::priority_thread_pool does)
|
||||
printf("[THREADPOOL] Creating %d threads (like KiCad's BS::priority_thread_pool)...\n", num_threads);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] Creating ' + $0 + ' threads...');
|
||||
}, num_threads);
|
||||
#endif
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::atomic<bool>> started(num_threads);
|
||||
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
started[i] = false;
|
||||
|
||||
printf("[THREADPOOL] Creating thread %d\n", i);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] Creating thread ' + $0);
|
||||
}, i);
|
||||
#endif
|
||||
|
||||
threads.emplace_back([i, &started]() {
|
||||
started[i] = true;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] Thread ' + $0 + ' started');
|
||||
}, i);
|
||||
#endif
|
||||
// Simulate some work
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
});
|
||||
}
|
||||
|
||||
printf("[THREADPOOL] All threads created, waiting for completion...\n");
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] All threads created, waiting for completion...');
|
||||
});
|
||||
#endif
|
||||
|
||||
// Join all threads (this is what thread pool destructor does)
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
printf("[THREADPOOL] SUCCESS - All %d threads completed!\n", num_threads);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] SUCCESS - All threads completed!');
|
||||
});
|
||||
#endif
|
||||
|
||||
// Create simple UI to show success
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxString msg = wxString::Format(
|
||||
"Thread Pool Test PASSED!\n\n"
|
||||
"Created and joined %d threads successfully.\n\n"
|
||||
"This test replicates KiCad's BS::priority_thread_pool pattern.\n"
|
||||
"If you see this message, the deadlock did NOT occur.",
|
||||
num_threads
|
||||
);
|
||||
|
||||
wxStaticText* label = new wxStaticText(panel, wxID_ANY, msg,
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
label->SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
|
||||
sizer->AddStretchSpacer();
|
||||
sizer->Add(label, 0, wxALIGN_CENTER | wxALL, 20);
|
||||
sizer->AddStretchSpacer();
|
||||
|
||||
panel->SetSizer(sizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText(wxString::Format("SUCCESS: %d threads created and joined", num_threads));
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadPoolApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
printf("[THREADPOOL] App OnInit starting\n");
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] App OnInit starting');
|
||||
});
|
||||
#endif
|
||||
|
||||
ThreadPoolFrame* frame = new ThreadPoolFrame();
|
||||
frame->Show();
|
||||
|
||||
printf("[THREADPOOL] App OnInit complete\n");
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[THREADPOOL] App OnInit complete');
|
||||
});
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(ThreadPoolApp);
|
||||
235
tests/apps/standalone/timer/timer_test.cpp
Normal file
235
tests/apps/standalone/timer/timer_test.cpp
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// wxTimer Test - Tests timer functionality in WASM
|
||||
// KiCad uses timers for animations, auto-save, and periodic updates
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/timer.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class TimerTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class TimerTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
TimerTestFrame();
|
||||
~TimerTestFrame();
|
||||
|
||||
private:
|
||||
wxTimer* m_timer;
|
||||
wxTimer* m_fastTimer;
|
||||
int m_counter;
|
||||
int m_fastCounter;
|
||||
wxStaticText* m_counterDisplay;
|
||||
wxStaticText* m_fastCounterDisplay;
|
||||
wxGauge* m_progressGauge;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnStartTimer(wxCommandEvent& evt);
|
||||
void OnStopTimer(wxCommandEvent& evt);
|
||||
void OnStartFastTimer(wxCommandEvent& evt);
|
||||
void OnStopFastTimer(wxCommandEvent& evt);
|
||||
void OnResetCounters(wxCommandEvent& evt);
|
||||
void OnTimer(wxTimerEvent& evt);
|
||||
void OnFastTimer(wxTimerEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_START_TIMER = wxID_HIGHEST + 1,
|
||||
ID_STOP_TIMER,
|
||||
ID_START_FAST_TIMER,
|
||||
ID_STOP_FAST_TIMER,
|
||||
ID_RESET_COUNTERS,
|
||||
ID_TIMER,
|
||||
ID_FAST_TIMER
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(TimerTestFrame, wxFrame)
|
||||
EVT_BUTTON(ID_START_TIMER, TimerTestFrame::OnStartTimer)
|
||||
EVT_BUTTON(ID_STOP_TIMER, TimerTestFrame::OnStopTimer)
|
||||
EVT_BUTTON(ID_START_FAST_TIMER, TimerTestFrame::OnStartFastTimer)
|
||||
EVT_BUTTON(ID_STOP_FAST_TIMER, TimerTestFrame::OnStopFastTimer)
|
||||
EVT_BUTTON(ID_RESET_COUNTERS, TimerTestFrame::OnResetCounters)
|
||||
EVT_TIMER(ID_TIMER, TimerTestFrame::OnTimer)
|
||||
EVT_TIMER(ID_FAST_TIMER, TimerTestFrame::OnFastTimer)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(TimerTestApp);
|
||||
|
||||
bool TimerTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
TimerTestFrame* frame = new TimerTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
TimerTestFrame::TimerTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxTimer WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 550))
|
||||
, m_counter(0)
|
||||
, m_fastCounter(0)
|
||||
{
|
||||
m_timer = new wxTimer(this, ID_TIMER);
|
||||
m_fastTimer = new wxTimer(this, ID_FAST_TIMER);
|
||||
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxTimer Test\n\n"
|
||||
"KiCad uses timers for auto-save, animations, and periodic updates.\n"
|
||||
"Test both slow (1 sec) and fast (100ms) timers.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Slow timer section (1 second interval)
|
||||
wxStaticBoxSizer* timerBox = new wxStaticBoxSizer(wxVERTICAL, this, "Slow Timer (1 second)");
|
||||
|
||||
wxBoxSizer* timerBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
timerBtnSizer->Add(new wxButton(this, ID_START_TIMER, "Start"), 0, wxALL, 5);
|
||||
timerBtnSizer->Add(new wxButton(this, ID_STOP_TIMER, "Stop"), 0, wxALL, 5);
|
||||
timerBox->Add(timerBtnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
m_counterDisplay = new wxStaticText(this, wxID_ANY, "Counter: 0",
|
||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
m_counterDisplay->SetFont(m_counterDisplay->GetFont().Scale(2.0));
|
||||
timerBox->Add(m_counterDisplay, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
mainSizer->Add(timerBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Fast timer section (100ms interval)
|
||||
wxStaticBoxSizer* fastTimerBox = new wxStaticBoxSizer(wxVERTICAL, this, "Fast Timer (100ms)");
|
||||
|
||||
wxBoxSizer* fastBtnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
fastBtnSizer->Add(new wxButton(this, ID_START_FAST_TIMER, "Start Fast"), 0, wxALL, 5);
|
||||
fastBtnSizer->Add(new wxButton(this, ID_STOP_FAST_TIMER, "Stop Fast"), 0, wxALL, 5);
|
||||
fastTimerBox->Add(fastBtnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
m_fastCounterDisplay = new wxStaticText(this, wxID_ANY, "Fast Counter: 0");
|
||||
fastTimerBox->Add(m_fastCounterDisplay, 0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
m_progressGauge = new wxGauge(this, wxID_ANY, 100);
|
||||
fastTimerBox->Add(m_progressGauge, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainSizer->Add(fastTimerBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Reset button
|
||||
mainSizer->Add(new wxButton(this, ID_RESET_COUNTERS, "Reset All Counters"),
|
||||
0, wxALIGN_CENTER | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 120), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
LogEvent("Timer test app started");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TIMER_TEST] wxTimer test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
TimerTestFrame::~TimerTestFrame()
|
||||
{
|
||||
m_timer->Stop();
|
||||
m_fastTimer->Stop();
|
||||
delete m_timer;
|
||||
delete m_fastTimer;
|
||||
}
|
||||
|
||||
void TimerTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TIMER_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnStartTimer(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_timer->Start(1000); // 1 second
|
||||
LogEvent("Slow timer started (1 second interval)");
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnStopTimer(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_timer->Stop();
|
||||
LogEvent("Slow timer stopped");
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnStartFastTimer(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_fastTimer->Start(100); // 100ms
|
||||
LogEvent("Fast timer started (100ms interval)");
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnStopFastTimer(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_fastTimer->Stop();
|
||||
LogEvent("Fast timer stopped");
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnResetCounters(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_counter = 0;
|
||||
m_fastCounter = 0;
|
||||
m_counterDisplay->SetLabel("Counter: 0");
|
||||
m_fastCounterDisplay->SetLabel("Fast Counter: 0");
|
||||
m_progressGauge->SetValue(0);
|
||||
LogEvent("Counters reset");
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnTimer(wxTimerEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_counter++;
|
||||
m_counterDisplay->SetLabel(wxString::Format("Counter: %d", m_counter));
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TIMER_TICK] Slow timer tick: ' + $0);
|
||||
}, m_counter);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TimerTestFrame::OnFastTimer(wxTimerEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_fastCounter++;
|
||||
m_fastCounterDisplay->SetLabel(wxString::Format("Fast Counter: %d", m_fastCounter));
|
||||
m_progressGauge->SetValue(m_fastCounter % 101);
|
||||
|
||||
// Log every 10 ticks to avoid flooding
|
||||
if (m_fastCounter % 10 == 0) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TIMER_TICK] Fast timer tick: ' + $0);
|
||||
}, m_fastCounter);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
188
tests/apps/standalone/toolbar/toolbar_test.cpp
Normal file
188
tests/apps/standalone/toolbar/toolbar_test.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
// wxToolBar/wxStatusBar Test - Tests toolbar and statusbar in WASM
|
||||
// KiCad uses toolbars extensively for actions
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/artprov.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class ToolbarTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class ToolbarTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
ToolbarTestFrame();
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
|
||||
void OnToolNew(wxCommandEvent& evt);
|
||||
void OnToolOpen(wxCommandEvent& evt);
|
||||
void OnToolSave(wxCommandEvent& evt);
|
||||
void OnToolZoomIn(wxCommandEvent& evt);
|
||||
void OnToolZoomOut(wxCommandEvent& evt);
|
||||
void OnToolToggle(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_TOOL_NEW = wxID_HIGHEST + 1,
|
||||
ID_TOOL_OPEN,
|
||||
ID_TOOL_SAVE,
|
||||
ID_TOOL_ZOOM_IN,
|
||||
ID_TOOL_ZOOM_OUT,
|
||||
ID_TOOL_TOGGLE
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(ToolbarTestFrame, wxFrame)
|
||||
EVT_TOOL(ID_TOOL_NEW, ToolbarTestFrame::OnToolNew)
|
||||
EVT_TOOL(ID_TOOL_OPEN, ToolbarTestFrame::OnToolOpen)
|
||||
EVT_TOOL(ID_TOOL_SAVE, ToolbarTestFrame::OnToolSave)
|
||||
EVT_TOOL(ID_TOOL_ZOOM_IN, ToolbarTestFrame::OnToolZoomIn)
|
||||
EVT_TOOL(ID_TOOL_ZOOM_OUT, ToolbarTestFrame::OnToolZoomOut)
|
||||
EVT_TOOL(ID_TOOL_TOGGLE, ToolbarTestFrame::OnToolToggle)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(ToolbarTestApp);
|
||||
|
||||
bool ToolbarTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
ToolbarTestFrame* frame = new ToolbarTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
ToolbarTestFrame::ToolbarTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxToolBar/wxStatusBar WASM Test",
|
||||
wxDefaultPosition, wxSize(700, 500))
|
||||
{
|
||||
// Create toolbar
|
||||
wxToolBar* toolbar = CreateToolBar(wxTB_HORIZONTAL | wxTB_TEXT);
|
||||
|
||||
toolbar->AddTool(ID_TOOL_NEW, "New",
|
||||
wxArtProvider::GetBitmap(wxART_NEW, wxART_TOOLBAR),
|
||||
"Create new file");
|
||||
toolbar->AddTool(ID_TOOL_OPEN, "Open",
|
||||
wxArtProvider::GetBitmap(wxART_FILE_OPEN, wxART_TOOLBAR),
|
||||
"Open existing file");
|
||||
toolbar->AddTool(ID_TOOL_SAVE, "Save",
|
||||
wxArtProvider::GetBitmap(wxART_FILE_SAVE, wxART_TOOLBAR),
|
||||
"Save current file");
|
||||
|
||||
toolbar->AddSeparator();
|
||||
|
||||
toolbar->AddTool(ID_TOOL_ZOOM_IN, "Zoom In",
|
||||
wxArtProvider::GetBitmap(wxART_PLUS, wxART_TOOLBAR),
|
||||
"Zoom in");
|
||||
toolbar->AddTool(ID_TOOL_ZOOM_OUT, "Zoom Out",
|
||||
wxArtProvider::GetBitmap(wxART_MINUS, wxART_TOOLBAR),
|
||||
"Zoom out");
|
||||
|
||||
toolbar->AddSeparator();
|
||||
|
||||
toolbar->AddCheckTool(ID_TOOL_TOGGLE, "Toggle",
|
||||
wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_TOOLBAR),
|
||||
wxNullBitmap,
|
||||
"Toggle tool example");
|
||||
|
||||
toolbar->Realize();
|
||||
|
||||
// Create status bar with multiple fields
|
||||
CreateStatusBar(3);
|
||||
SetStatusText("Ready", 0);
|
||||
SetStatusText("X: 0, Y: 0", 1);
|
||||
SetStatusText("Zoom: 100%", 2);
|
||||
|
||||
// Set status bar field widths
|
||||
int widths[] = {-1, 150, 100};
|
||||
GetStatusBar()->SetStatusWidths(3, widths);
|
||||
|
||||
// Main content
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxToolBar and wxStatusBar Test\n\n"
|
||||
"KiCad uses toolbars for quick access to tools.\n"
|
||||
"Click toolbar buttons to see events.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
|
||||
LogEvent("Toolbar test app started");
|
||||
LogEvent("Toolbar created with 6 tools");
|
||||
LogEvent("Status bar created with 3 fields");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TOOLBAR_TEST] wxToolBar test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg, 0);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TOOLBAR_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolNew(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Toolbar: New clicked");
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolOpen(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Toolbar: Open clicked");
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolSave(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Toolbar: Save clicked");
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolZoomIn(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Toolbar: Zoom In clicked");
|
||||
SetStatusText("Zoom: 150%", 2);
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolZoomOut(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
LogEvent("Toolbar: Zoom Out clicked");
|
||||
SetStatusText("Zoom: 75%", 2);
|
||||
}
|
||||
|
||||
void ToolbarTestFrame::OnToolToggle(wxCommandEvent& evt)
|
||||
{
|
||||
bool checked = evt.IsChecked();
|
||||
LogEvent(wxString::Format("Toolbar: Toggle %s", checked ? "ON" : "OFF"));
|
||||
}
|
||||
256
tests/apps/standalone/tree/tree_test.cpp
Normal file
256
tests/apps/standalone/tree/tree_test.cpp
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
// wxTreeCtrl Test - Tests tree control in WASM
|
||||
// KiCad uses tree controls for hierarchy browsers (components, nets, etc.)
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/treectrl.h"
|
||||
#include "wx/imaglist.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
class TreeTestApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override;
|
||||
};
|
||||
|
||||
class TreeTestFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
TreeTestFrame();
|
||||
|
||||
private:
|
||||
wxTreeCtrl* m_tree;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
void LogEvent(const wxString& msg);
|
||||
void PopulateTree();
|
||||
|
||||
void OnSelChanged(wxTreeEvent& evt);
|
||||
void OnItemExpanding(wxTreeEvent& evt);
|
||||
void OnItemCollapsing(wxTreeEvent& evt);
|
||||
void OnItemActivated(wxTreeEvent& evt);
|
||||
void OnExpandAll(wxCommandEvent& evt);
|
||||
void OnCollapseAll(wxCommandEvent& evt);
|
||||
void OnAddItem(wxCommandEvent& evt);
|
||||
void OnDeleteItem(wxCommandEvent& evt);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_TREE = wxID_HIGHEST + 1,
|
||||
ID_EXPAND_ALL,
|
||||
ID_COLLAPSE_ALL,
|
||||
ID_ADD_ITEM,
|
||||
ID_DELETE_ITEM
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(TreeTestFrame, wxFrame)
|
||||
EVT_TREE_SEL_CHANGED(ID_TREE, TreeTestFrame::OnSelChanged)
|
||||
EVT_TREE_ITEM_EXPANDING(ID_TREE, TreeTestFrame::OnItemExpanding)
|
||||
EVT_TREE_ITEM_COLLAPSING(ID_TREE, TreeTestFrame::OnItemCollapsing)
|
||||
EVT_TREE_ITEM_ACTIVATED(ID_TREE, TreeTestFrame::OnItemActivated)
|
||||
EVT_BUTTON(ID_EXPAND_ALL, TreeTestFrame::OnExpandAll)
|
||||
EVT_BUTTON(ID_COLLAPSE_ALL, TreeTestFrame::OnCollapseAll)
|
||||
EVT_BUTTON(ID_ADD_ITEM, TreeTestFrame::OnAddItem)
|
||||
EVT_BUTTON(ID_DELETE_ITEM, TreeTestFrame::OnDeleteItem)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
wxIMPLEMENT_APP(TreeTestApp);
|
||||
|
||||
bool TreeTestApp::OnInit()
|
||||
{
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
TreeTestFrame* frame = new TreeTestFrame();
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeTestFrame::TreeTestFrame()
|
||||
: wxFrame(nullptr, wxID_ANY, "wxTreeCtrl WASM Test",
|
||||
wxDefaultPosition, wxSize(600, 600))
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxStaticText* desc = new wxStaticText(this, wxID_ANY,
|
||||
"wxTreeCtrl Test\n\n"
|
||||
"KiCad uses tree controls for hierarchy browsers, component trees, and net lists.\n"
|
||||
"Click items to select, double-click to activate, +/- to expand/collapse.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Button bar
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnSizer->Add(new wxButton(this, ID_EXPAND_ALL, "Expand All"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_COLLAPSE_ALL, "Collapse All"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_ADD_ITEM, "Add Item"), 0, wxALL, 5);
|
||||
btnSizer->Add(new wxButton(this, ID_DELETE_ITEM, "Delete Selected"), 0, wxALL, 5);
|
||||
mainSizer->Add(btnSizer, 0, wxALIGN_CENTER);
|
||||
|
||||
// Tree control
|
||||
m_tree = new wxTreeCtrl(this, ID_TREE, wxDefaultPosition, wxSize(-1, 250),
|
||||
wxTR_DEFAULT_STYLE | wxTR_EDIT_LABELS);
|
||||
mainSizer->Add(m_tree, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
PopulateTree();
|
||||
|
||||
// Event log
|
||||
wxStaticBoxSizer* logBox = new wxStaticBoxSizer(wxVERTICAL, this, "Event Log");
|
||||
m_log = new wxTextCtrl(this, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE | wxTE_READONLY);
|
||||
logBox->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
mainSizer->Add(logBox, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Ready");
|
||||
|
||||
LogEvent("Tree test app started");
|
||||
LogEvent("Tree populated with KiCad-like hierarchy");
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
EM_ASM({
|
||||
console.log('[TREE_TEST] wxTreeCtrl test app started successfully');
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void TreeTestFrame::PopulateTree()
|
||||
{
|
||||
// Create a KiCad-like component hierarchy
|
||||
wxTreeItemId root = m_tree->AddRoot("Project: MyBoard");
|
||||
|
||||
// Schematic hierarchy
|
||||
wxTreeItemId schematic = m_tree->AppendItem(root, "Schematic");
|
||||
wxTreeItemId sheet1 = m_tree->AppendItem(schematic, "Sheet 1 - Main");
|
||||
m_tree->AppendItem(sheet1, "U1 - MCU");
|
||||
m_tree->AppendItem(sheet1, "U2 - Power Regulator");
|
||||
m_tree->AppendItem(sheet1, "C1-C10 - Capacitors");
|
||||
m_tree->AppendItem(sheet1, "R1-R20 - Resistors");
|
||||
|
||||
wxTreeItemId sheet2 = m_tree->AppendItem(schematic, "Sheet 2 - IO");
|
||||
m_tree->AppendItem(sheet2, "J1 - USB Connector");
|
||||
m_tree->AppendItem(sheet2, "J2 - GPIO Header");
|
||||
m_tree->AppendItem(sheet2, "LED1-LED4 - Status LEDs");
|
||||
|
||||
// PCB hierarchy
|
||||
wxTreeItemId pcb = m_tree->AppendItem(root, "PCB");
|
||||
wxTreeItemId layers = m_tree->AppendItem(pcb, "Layers");
|
||||
m_tree->AppendItem(layers, "F.Cu - Front Copper");
|
||||
m_tree->AppendItem(layers, "B.Cu - Back Copper");
|
||||
m_tree->AppendItem(layers, "F.SilkS - Front Silkscreen");
|
||||
m_tree->AppendItem(layers, "B.SilkS - Back Silkscreen");
|
||||
m_tree->AppendItem(layers, "Edge.Cuts - Board Outline");
|
||||
|
||||
wxTreeItemId nets = m_tree->AppendItem(pcb, "Nets");
|
||||
m_tree->AppendItem(nets, "GND (45 pads)");
|
||||
m_tree->AppendItem(nets, "VCC (12 pads)");
|
||||
m_tree->AppendItem(nets, "3V3 (8 pads)");
|
||||
m_tree->AppendItem(nets, "SDA (4 pads)");
|
||||
m_tree->AppendItem(nets, "SCL (4 pads)");
|
||||
|
||||
// Libraries
|
||||
wxTreeItemId libraries = m_tree->AppendItem(root, "Libraries");
|
||||
m_tree->AppendItem(libraries, "Device.lib");
|
||||
m_tree->AppendItem(libraries, "Connector.lib");
|
||||
m_tree->AppendItem(libraries, "MCU_ST.lib");
|
||||
|
||||
m_tree->Expand(root);
|
||||
m_tree->Expand(schematic);
|
||||
m_tree->Expand(pcb);
|
||||
}
|
||||
|
||||
void TreeTestFrame::LogEvent(const wxString& msg)
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Always log to console, even during early initialization
|
||||
EM_ASM({
|
||||
console.log('[TREE_EVENT] ' + UTF8ToString($0));
|
||||
}, msg.c_str().AsChar());
|
||||
#endif
|
||||
|
||||
// Guard against events firing before m_log is initialized
|
||||
if (!m_log)
|
||||
return;
|
||||
m_log->AppendText(msg + "\n");
|
||||
SetStatusText(msg);
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnSelChanged(wxTreeEvent& evt)
|
||||
{
|
||||
wxTreeItemId item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
LogEvent(wxString::Format("Selection changed: '%s'", m_tree->GetItemText(item)));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnItemExpanding(wxTreeEvent& evt)
|
||||
{
|
||||
wxTreeItemId item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
LogEvent(wxString::Format("Expanding: '%s'", m_tree->GetItemText(item)));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnItemCollapsing(wxTreeEvent& evt)
|
||||
{
|
||||
wxTreeItemId item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
LogEvent(wxString::Format("Collapsing: '%s'", m_tree->GetItemText(item)));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnItemActivated(wxTreeEvent& evt)
|
||||
{
|
||||
wxTreeItemId item = evt.GetItem();
|
||||
if (item.IsOk()) {
|
||||
LogEvent(wxString::Format("Activated (double-click): '%s'", m_tree->GetItemText(item)));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnExpandAll(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_tree->ExpandAll();
|
||||
LogEvent("All items expanded");
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnCollapseAll(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
m_tree->CollapseAll();
|
||||
LogEvent("All items collapsed");
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnAddItem(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxTreeItemId sel = m_tree->GetSelection();
|
||||
if (sel.IsOk()) {
|
||||
static int itemNum = 1;
|
||||
wxTreeItemId newItem = m_tree->AppendItem(sel,
|
||||
wxString::Format("New Item %d", itemNum++));
|
||||
m_tree->Expand(sel);
|
||||
m_tree->SelectItem(newItem);
|
||||
LogEvent(wxString::Format("Added new item under '%s'", m_tree->GetItemText(sel)));
|
||||
} else {
|
||||
LogEvent("No item selected - select a parent first");
|
||||
}
|
||||
}
|
||||
|
||||
void TreeTestFrame::OnDeleteItem(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
wxTreeItemId sel = m_tree->GetSelection();
|
||||
if (sel.IsOk() && sel != m_tree->GetRootItem()) {
|
||||
wxString itemText = m_tree->GetItemText(sel);
|
||||
m_tree->Delete(sel);
|
||||
LogEvent(wxString::Format("Deleted item: '%s'", itemText));
|
||||
} else {
|
||||
LogEvent("Cannot delete root or no item selected");
|
||||
}
|
||||
}
|
||||
281
tests/apps/standalone/validators/validators_test.cpp
Normal file
281
tests/apps/standalone/validators/validators_test.cpp
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
// wxValidator Test - Input validation like KiCad uses
|
||||
// Tests: wxTextValidator, wxIntegerValidator, wxFloatingPointValidator, custom validators
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/valtext.h"
|
||||
#include "wx/valnum.h"
|
||||
|
||||
// Custom validator similar to KiCad's NETNAME_VALIDATOR
|
||||
class NetNameValidator : public wxTextValidator
|
||||
{
|
||||
public:
|
||||
NetNameValidator() : wxTextValidator(wxFILTER_NONE)
|
||||
{
|
||||
// Allow alphanumeric, underscore, and some special chars
|
||||
SetCharIncludes("_+-/");
|
||||
}
|
||||
|
||||
virtual wxObject* Clone() const override
|
||||
{
|
||||
return new NetNameValidator(*this);
|
||||
}
|
||||
|
||||
virtual bool Validate(wxWindow* parent) override
|
||||
{
|
||||
wxTextCtrl* tc = dynamic_cast<wxTextCtrl*>(GetWindow());
|
||||
if (!tc) return true;
|
||||
|
||||
wxString val = tc->GetValue();
|
||||
|
||||
// Net name cannot start with a number
|
||||
if (!val.IsEmpty() && wxIsdigit(val[0]))
|
||||
{
|
||||
wxMessageBox("Net name cannot start with a digit", "Validation Error",
|
||||
wxOK | wxICON_ERROR, parent);
|
||||
return false;
|
||||
}
|
||||
|
||||
return wxTextValidator::Validate(parent);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom validator for footprint names (like FOOTPRINT_NAME_VALIDATOR)
|
||||
class FootprintNameValidator : public wxTextValidator
|
||||
{
|
||||
public:
|
||||
FootprintNameValidator() : wxTextValidator(wxFILTER_ALPHANUMERIC)
|
||||
{
|
||||
SetCharIncludes("_-.");
|
||||
}
|
||||
|
||||
virtual wxObject* Clone() const override
|
||||
{
|
||||
return new FootprintNameValidator(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class ValidatorsFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
ValidatorsFrame() : wxFrame(nullptr, wxID_ANY, "wxValidator Test",
|
||||
wxDefaultPosition, wxSize(700, 600))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses validators for input validation in dialogs.\n"
|
||||
"Tests: wxTextValidator, wxIntegerValidator, wxFloatingPointValidator, custom validators.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Grid for input fields
|
||||
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 10, 10);
|
||||
gridSizer->AddGrowableCol(1, 1);
|
||||
|
||||
// 1. Alpha-numeric validator
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Alphanumeric only:"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_alphaCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
wxTextValidator(wxFILTER_ALPHANUMERIC));
|
||||
m_alphaCtrl->SetHint("Letters and numbers only");
|
||||
gridSizer->Add(m_alphaCtrl, 1, wxEXPAND);
|
||||
|
||||
// 2. Numeric only validator
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Digits only:"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_digitCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
wxTextValidator(wxFILTER_DIGITS));
|
||||
m_digitCtrl->SetHint("0-9 only");
|
||||
gridSizer->Add(m_digitCtrl, 1, wxEXPAND);
|
||||
|
||||
// 3. Integer validator with range
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Integer (0-1000):"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_intValue = 100;
|
||||
wxIntegerValidator<int> intValidator(&m_intValue);
|
||||
intValidator.SetRange(0, 1000);
|
||||
m_intCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "100",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
intValidator);
|
||||
gridSizer->Add(m_intCtrl, 1, wxEXPAND);
|
||||
|
||||
// 4. Float validator with range
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Float (0.0-100.0):"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_floatValue = 50.0;
|
||||
wxFloatingPointValidator<double> floatValidator(3, &m_floatValue);
|
||||
floatValidator.SetRange(0.0, 100.0);
|
||||
m_floatCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "50.0",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
floatValidator);
|
||||
gridSizer->Add(m_floatCtrl, 1, wxEXPAND);
|
||||
|
||||
// 5. Include chars validator
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Email chars (a-z, @, .):"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
wxTextValidator emailValidator(wxFILTER_ALPHANUMERIC);
|
||||
emailValidator.SetCharIncludes("@._-");
|
||||
m_emailCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
emailValidator);
|
||||
m_emailCtrl->SetHint("user@example.com");
|
||||
gridSizer->Add(m_emailCtrl, 1, wxEXPAND);
|
||||
|
||||
// 6. Exclude chars validator
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "No spaces allowed:"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
wxTextValidator noSpaceValidator(wxFILTER_EXCLUDE_CHAR_LIST);
|
||||
noSpaceValidator.SetCharExcludes(" \t\n");
|
||||
m_noSpaceCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
noSpaceValidator);
|
||||
m_noSpaceCtrl->SetHint("No whitespace");
|
||||
gridSizer->Add(m_noSpaceCtrl, 1, wxEXPAND);
|
||||
|
||||
// 7. Net name validator (custom)
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Net name (custom):"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_netNameCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
NetNameValidator());
|
||||
m_netNameCtrl->SetHint("Cannot start with digit");
|
||||
gridSizer->Add(m_netNameCtrl, 1, wxEXPAND);
|
||||
|
||||
// 8. Footprint name validator (custom)
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Footprint name:"),
|
||||
0, wxALIGN_CENTER_VERTICAL);
|
||||
m_footprintCtrl = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize, 0,
|
||||
FootprintNameValidator());
|
||||
m_footprintCtrl->SetHint("Alphanumeric with _-.");
|
||||
gridSizer->Add(m_footprintCtrl, 1, wxEXPAND);
|
||||
|
||||
mainSizer->Add(gridSizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btnValidate = new wxButton(mainPanel, wxID_ANY, "Validate All");
|
||||
wxButton* btnTransfer = new wxButton(mainPanel, wxID_ANY, "Transfer Data");
|
||||
wxButton* btnClear = new wxButton(mainPanel, wxID_ANY, "Clear All");
|
||||
|
||||
btnValidate->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnValidateAll, this);
|
||||
btnTransfer->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnTransferData, this);
|
||||
btnClear->Bind(wxEVT_BUTTON, &ValidatorsFrame::OnClearAll, this);
|
||||
|
||||
btnSizer->Add(btnValidate, 0, wxRIGHT, 5);
|
||||
btnSizer->Add(btnTransfer, 0, wxRIGHT, 5);
|
||||
btnSizer->Add(btnClear, 0);
|
||||
|
||||
mainSizer->Add(btnSizer, 0, wxALL, 10);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("Validator test app started");
|
||||
Log("Validator test app started");
|
||||
Log("Try typing invalid characters - they should be blocked");
|
||||
}
|
||||
|
||||
private:
|
||||
void OnValidateAll(wxCommandEvent& event)
|
||||
{
|
||||
Log("Validating all fields...");
|
||||
|
||||
bool allValid = true;
|
||||
|
||||
// Validate each control
|
||||
wxTextCtrl* controls[] = {m_alphaCtrl, m_digitCtrl, m_intCtrl, m_floatCtrl,
|
||||
m_emailCtrl, m_noSpaceCtrl, m_netNameCtrl, m_footprintCtrl};
|
||||
const char* names[] = {"Alphanumeric", "Digits", "Integer", "Float",
|
||||
"Email", "NoSpace", "NetName", "Footprint"};
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
wxValidator* validator = controls[i]->GetValidator();
|
||||
if (validator)
|
||||
{
|
||||
bool valid = validator->Validate(this);
|
||||
Log(wxString::Format(" %s: %s", names[i], valid ? "VALID" : "INVALID"));
|
||||
if (!valid) allValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
Log(wxString::Format("Overall result: %s", allValid ? "ALL VALID" : "SOME INVALID"));
|
||||
|
||||
if (allValid)
|
||||
{
|
||||
wxMessageBox("All fields are valid!", "Validation", wxOK | wxICON_INFORMATION);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTransferData(wxCommandEvent& event)
|
||||
{
|
||||
Log("Transferring data from controls...");
|
||||
|
||||
// TransferDataFromWindow updates the bound variables
|
||||
if (TransferDataFromWindow())
|
||||
{
|
||||
Log(wxString::Format(" Integer value: %d", m_intValue));
|
||||
Log(wxString::Format(" Float value: %.3f", m_floatValue));
|
||||
Log("Transfer successful");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Transfer failed - validation error");
|
||||
}
|
||||
}
|
||||
|
||||
void OnClearAll(wxCommandEvent& event)
|
||||
{
|
||||
m_alphaCtrl->Clear();
|
||||
m_digitCtrl->Clear();
|
||||
m_intCtrl->SetValue("0");
|
||||
m_floatCtrl->SetValue("0.0");
|
||||
m_emailCtrl->Clear();
|
||||
m_noSpaceCtrl->Clear();
|
||||
m_netNameCtrl->Clear();
|
||||
m_footprintCtrl->Clear();
|
||||
Log("All fields cleared");
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxTextCtrl* m_alphaCtrl;
|
||||
wxTextCtrl* m_digitCtrl;
|
||||
wxTextCtrl* m_intCtrl;
|
||||
wxTextCtrl* m_floatCtrl;
|
||||
wxTextCtrl* m_emailCtrl;
|
||||
wxTextCtrl* m_noSpaceCtrl;
|
||||
wxTextCtrl* m_netNameCtrl;
|
||||
wxTextCtrl* m_footprintCtrl;
|
||||
wxTextCtrl* m_log;
|
||||
|
||||
int m_intValue;
|
||||
double m_floatValue;
|
||||
};
|
||||
|
||||
class ValidatorsApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
ValidatorsFrame* frame = new ValidatorsFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(ValidatorsApp);
|
||||
461
tests/apps/standalone/wasmedge/wasmedge_test.cpp
Normal file
461
tests/apps/standalone/wasmedge/wasmedge_test.cpp
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
// WASM Edge Cases Test - Test WASM-specific behaviors and limitations
|
||||
// Tests: Threading stubs, file system, memory, asyncify, clipboard permissions
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/file.h"
|
||||
#include "wx/filename.h"
|
||||
#include "wx/dir.h"
|
||||
#include "wx/clipbrd.h"
|
||||
#include "wx/thread.h"
|
||||
#include "wx/utils.h"
|
||||
#include "wx/fontenum.h"
|
||||
|
||||
// Test if threading is stubbed or functional
|
||||
class TestThread : public wxThread
|
||||
{
|
||||
public:
|
||||
TestThread(wxTextCtrl* log) : wxThread(wxTHREAD_DETACHED), m_log(log) {}
|
||||
|
||||
virtual void* Entry() override
|
||||
{
|
||||
// In WASM, this may not actually run in a separate thread
|
||||
m_ran = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DidRun() const { return m_ran; }
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_log;
|
||||
bool m_ran = false;
|
||||
};
|
||||
|
||||
class WasmEdgeFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
WasmEdgeFrame() : wxFrame(nullptr, wxID_ANY, "WASM Edge Cases Test",
|
||||
wxDefaultPosition, wxSize(800, 700))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"Tests WASM-specific behaviors: threading stubs, file limits, memory, asyncify.\n"
|
||||
"These tests verify WASM port handles browser limitations correctly.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Test buttons
|
||||
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 10, 10);
|
||||
gridSizer->AddGrowableCol(1, 1);
|
||||
|
||||
// File System Tests
|
||||
wxButton* btnFileWrite = new wxButton(mainPanel, wxID_ANY, "Test File Write (/tmp/)");
|
||||
btnFileWrite->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileWrite, this);
|
||||
gridSizer->Add(btnFileWrite, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Write to WASM virtual file system"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
wxButton* btnFileRead = new wxButton(mainPanel, wxID_ANY, "Test File Read");
|
||||
btnFileRead->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileRead, this);
|
||||
gridSizer->Add(btnFileRead, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Read from virtual file system"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
wxButton* btnDirList = new wxButton(mainPanel, wxID_ANY, "Test Dir Listing");
|
||||
btnDirList->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestDirListing, this);
|
||||
gridSizer->Add(btnDirList, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "List /tmp/ directory contents"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Threading Tests
|
||||
wxButton* btnThread = new wxButton(mainPanel, wxID_ANY, "Test Threading");
|
||||
btnThread->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestThreading, this);
|
||||
gridSizer->Add(btnThread, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Check if wxThread is stubbed"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Font Enumeration Tests
|
||||
wxButton* btnFonts = new wxButton(mainPanel, wxID_ANY, "Test Font Enumeration");
|
||||
btnFonts->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFontEnum, this);
|
||||
gridSizer->Add(btnFonts, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Enumerate available fonts (may fail in WASM)"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Clipboard Tests
|
||||
wxButton* btnClipboard = new wxButton(mainPanel, wxID_ANY, "Test Clipboard");
|
||||
btnClipboard->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestClipboard, this);
|
||||
gridSizer->Add(btnClipboard, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test clipboard with asyncify"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Memory Tests
|
||||
wxButton* btnMemory = new wxButton(mainPanel, wxID_ANY, "Test Memory Allocation");
|
||||
btnMemory->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestMemory, this);
|
||||
gridSizer->Add(btnMemory, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test WASM memory growth"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// OS Info Tests
|
||||
wxButton* btnOsInfo = new wxButton(mainPanel, wxID_ANY, "Test OS Info");
|
||||
btnOsInfo->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestOsInfo, this);
|
||||
gridSizer->Add(btnOsInfo, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Check wxGetOsVersion (may be stubbed)"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// wxLaunchDefaultBrowser Test
|
||||
wxButton* btnBrowser = new wxButton(mainPanel, wxID_ANY, "Test URL Launch");
|
||||
btnBrowser->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestBrowserLaunch, this);
|
||||
gridSizer->Add(btnBrowser, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test wxLaunchDefaultBrowser"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// wxFileName Tests
|
||||
wxButton* btnFileName = new wxButton(mainPanel, wxID_ANY, "Test wxFileName");
|
||||
btnFileName->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnTestFileName, this);
|
||||
gridSizer->Add(btnFileName, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Path manipulation functions"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Run All Tests
|
||||
wxButton* btnAll = new wxButton(mainPanel, wxID_ANY, "Run All Tests");
|
||||
btnAll->Bind(wxEVT_BUTTON, &WasmEdgeFrame::OnRunAllTests, this);
|
||||
gridSizer->Add(btnAll, 0, wxEXPAND);
|
||||
gridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Execute all edge case tests"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
mainSizer->Add(gridSizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Test Results"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 300),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
m_log->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("WASM edge cases test app started");
|
||||
Log("WASM Edge Cases Test App Started");
|
||||
Log("====================================\n");
|
||||
}
|
||||
|
||||
private:
|
||||
void OnTestFileWrite(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== File Write Test ===");
|
||||
|
||||
wxString testFile = "/tmp/wasm_test_file.txt";
|
||||
wxString content = "Hello from WASM!\nLine 2\nLine 3";
|
||||
|
||||
wxFile file;
|
||||
if (file.Create(testFile, true)) // true = overwrite
|
||||
{
|
||||
if (file.Write(content))
|
||||
{
|
||||
Log("SUCCESS: Wrote " + wxString::Format("%zu", content.Length()) + " bytes to " + testFile);
|
||||
m_testFilePath = testFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not write to file");
|
||||
}
|
||||
file.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not create file " + testFile);
|
||||
}
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestFileRead(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== File Read Test ===");
|
||||
|
||||
if (m_testFilePath.IsEmpty())
|
||||
{
|
||||
Log("No test file - run File Write test first");
|
||||
Log("");
|
||||
return;
|
||||
}
|
||||
|
||||
wxFile file;
|
||||
if (file.Open(m_testFilePath))
|
||||
{
|
||||
wxString content;
|
||||
if (file.ReadAll(&content))
|
||||
{
|
||||
Log("SUCCESS: Read " + wxString::Format("%zu", content.Length()) + " bytes");
|
||||
Log("Content:\n" + content);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not read file content");
|
||||
}
|
||||
file.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not open " + m_testFilePath);
|
||||
}
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestDirListing(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== Directory Listing Test ===");
|
||||
|
||||
wxDir dir("/tmp");
|
||||
if (dir.IsOpened())
|
||||
{
|
||||
Log("Contents of /tmp/:");
|
||||
|
||||
wxString filename;
|
||||
int count = 0;
|
||||
bool cont = dir.GetFirst(&filename);
|
||||
while (cont)
|
||||
{
|
||||
Log(" " + filename);
|
||||
count++;
|
||||
cont = dir.GetNext(&filename);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
Log(" (empty directory)");
|
||||
else
|
||||
Log(wxString::Format(" Total: %d files", count));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not open /tmp/ directory");
|
||||
}
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestThreading(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== Threading Test ===");
|
||||
|
||||
// In WASM, threading may be stubbed
|
||||
Log("Creating wxThread...");
|
||||
|
||||
// Check if we can create a thread (may be no-op in WASM)
|
||||
#if wxUSE_THREADS
|
||||
Log("wxUSE_THREADS is defined");
|
||||
|
||||
// Note: Actually running threads in WASM is complex
|
||||
// This test just checks if the API is available
|
||||
Log("Thread API is available (may be stubbed in WASM)");
|
||||
Log("WASM typically runs single-threaded");
|
||||
Log("For async operations, use wxTimer or emscripten_async_*");
|
||||
#else
|
||||
Log("wxUSE_THREADS is NOT defined");
|
||||
#endif
|
||||
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestFontEnum(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== Font Enumeration Test ===");
|
||||
|
||||
class FontEnumerator : public wxFontEnumerator
|
||||
{
|
||||
public:
|
||||
wxArrayString fonts;
|
||||
|
||||
virtual bool OnFacename(const wxString& facename) override
|
||||
{
|
||||
fonts.Add(facename);
|
||||
return true; // Continue enumeration
|
||||
}
|
||||
};
|
||||
|
||||
FontEnumerator enumerator;
|
||||
bool result = enumerator.EnumerateFacenames();
|
||||
|
||||
if (result && enumerator.fonts.GetCount() > 0)
|
||||
{
|
||||
Log("SUCCESS: Found " + wxString::Format("%zu", enumerator.fonts.GetCount()) + " fonts:");
|
||||
for (size_t i = 0; i < wxMin(enumerator.fonts.GetCount(), (size_t)10); i++)
|
||||
{
|
||||
Log(" " + enumerator.fonts[i]);
|
||||
}
|
||||
if (enumerator.fonts.GetCount() > 10)
|
||||
Log(" ... and " + wxString::Format("%zu", enumerator.fonts.GetCount() - 10) + " more");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("NOTICE: Font enumeration returned false or empty");
|
||||
Log("This is expected in WASM - fontenum.cpp returns false");
|
||||
Log("Font pickers should use a predefined font list instead");
|
||||
}
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestClipboard(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== Clipboard Test ===");
|
||||
|
||||
wxString testText = "WASM Clipboard Test " + wxDateTime::Now().FormatISOCombined();
|
||||
|
||||
if (wxTheClipboard->Open())
|
||||
{
|
||||
// Write
|
||||
wxTheClipboard->SetData(new wxTextDataObject(testText));
|
||||
Log("Wrote to clipboard: " + testText);
|
||||
|
||||
// Read back
|
||||
if (wxTheClipboard->IsSupported(wxDF_TEXT))
|
||||
{
|
||||
wxTextDataObject data;
|
||||
if (wxTheClipboard->GetData(data))
|
||||
{
|
||||
Log("Read from clipboard: " + data.GetText());
|
||||
if (data.GetText() == testText)
|
||||
Log("SUCCESS: Clipboard round-trip works");
|
||||
else
|
||||
Log("WARNING: Read text differs from written text");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("NOTICE: Could not read clipboard (may need user interaction)");
|
||||
}
|
||||
}
|
||||
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("FAILED: Could not open clipboard");
|
||||
}
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestMemory(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== Memory Allocation Test ===");
|
||||
|
||||
// Test small allocation
|
||||
std::vector<char> small(1024 * 10); // 10 KB
|
||||
Log("Allocated 10 KB: SUCCESS");
|
||||
|
||||
// Test medium allocation
|
||||
std::vector<char> medium(1024 * 1024); // 1 MB
|
||||
Log("Allocated 1 MB: SUCCESS");
|
||||
|
||||
// Test larger allocation (WASM memory growth)
|
||||
try
|
||||
{
|
||||
std::vector<char> large(1024 * 1024 * 10); // 10 MB
|
||||
Log("Allocated 10 MB: SUCCESS (WASM memory growth works)");
|
||||
}
|
||||
catch (const std::bad_alloc& e)
|
||||
{
|
||||
Log("FAILED: Could not allocate 10 MB");
|
||||
Log(" Error: " + wxString(e.what()));
|
||||
}
|
||||
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestOsInfo(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== OS Info Test ===");
|
||||
|
||||
int major, minor, micro;
|
||||
wxOperatingSystemId os = wxGetOsVersion(&major, &minor, µ);
|
||||
|
||||
Log("wxGetOsVersion returned:");
|
||||
Log(" OS ID: " + wxString::Format("%d", (int)os));
|
||||
Log(" Version: " + wxString::Format("%d.%d.%d", major, minor, micro));
|
||||
|
||||
wxString osDesc = wxGetOsDescription();
|
||||
Log(" Description: " + osDesc);
|
||||
|
||||
// In WASM, these may be stubbed
|
||||
if (osDesc.IsEmpty() || osDesc == "Unknown")
|
||||
{
|
||||
Log("NOTICE: OS info may be stubbed in WASM");
|
||||
}
|
||||
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestBrowserLaunch(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== URL Launch Test ===");
|
||||
|
||||
// In WASM, this should open in new tab via window.open()
|
||||
wxString url = "https://www.kicad.org";
|
||||
|
||||
Log("Attempting to open: " + url);
|
||||
bool result = wxLaunchDefaultBrowser(url);
|
||||
|
||||
if (result)
|
||||
Log("SUCCESS: Browser launch returned true");
|
||||
else
|
||||
Log("NOTICE: Browser launch returned false (may still work via popup)");
|
||||
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnTestFileName(wxCommandEvent& event)
|
||||
{
|
||||
Log("=== wxFileName Test ===");
|
||||
|
||||
// Test path manipulation
|
||||
wxFileName fn("/tmp/test/file.kicad_pcb");
|
||||
|
||||
Log("Full path: " + fn.GetFullPath());
|
||||
Log("Name: " + fn.GetName());
|
||||
Log("Extension: " + fn.GetExt());
|
||||
Log("Path: " + fn.GetPath());
|
||||
Log("Volume: " + fn.GetVolume());
|
||||
|
||||
// Test path building
|
||||
wxFileName fn2;
|
||||
fn2.AssignDir("/home/user/projects");
|
||||
fn2.AppendDir("kicad");
|
||||
fn2.SetFullName("board.kicad_pcb");
|
||||
|
||||
Log("Built path: " + fn2.GetFullPath());
|
||||
|
||||
// Test relative path
|
||||
wxFileName relative;
|
||||
relative.Assign("../designs/board.kicad_pcb");
|
||||
Log("Is relative: " + wxString(relative.IsRelative() ? "yes" : "no"));
|
||||
|
||||
Log("");
|
||||
}
|
||||
|
||||
void OnRunAllTests(wxCommandEvent& event)
|
||||
{
|
||||
m_log->Clear();
|
||||
Log("Running all WASM edge case tests...\n");
|
||||
|
||||
OnTestFileWrite(event);
|
||||
OnTestFileRead(event);
|
||||
OnTestDirListing(event);
|
||||
OnTestThreading(event);
|
||||
OnTestFontEnum(event);
|
||||
OnTestClipboard(event);
|
||||
OnTestMemory(event);
|
||||
OnTestOsInfo(event);
|
||||
OnTestFileName(event);
|
||||
|
||||
Log("=== All Tests Complete ===");
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxTextCtrl* m_log;
|
||||
wxString m_testFilePath;
|
||||
};
|
||||
|
||||
class WasmEdgeApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
WasmEdgeFrame* frame = new WasmEdgeFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(WasmEdgeApp);
|
||||
290
tests/apps/standalone/wizard/wizard_test.cpp
Normal file
290
tests/apps/standalone/wizard/wizard_test.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// wxWizard Test - Footprint Wizard simulation
|
||||
// Tests wxWizard for step-by-step dialogs
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/wizard.h"
|
||||
#include "wx/spinctrl.h"
|
||||
|
||||
// Page 1: Package Type Selection
|
||||
class PackageTypePage : public wxWizardPageSimple
|
||||
{
|
||||
public:
|
||||
PackageTypePage(wxWizard* parent) : wxWizardPageSimple(parent)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 1: Select Package Type"), 0, wxBOTTOM, 10);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Choose the type of footprint to create:"), 0, wxBOTTOM, 10);
|
||||
|
||||
wxString choices[] = {"QFP (Quad Flat Package)", "BGA (Ball Grid Array)",
|
||||
"DIP (Dual In-line Package)", "SOT (Small Outline Transistor)",
|
||||
"SOP (Small Outline Package)"};
|
||||
m_choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 5, choices);
|
||||
m_choice->SetSelection(0);
|
||||
sizer->Add(m_choice, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Selected: QFP - Common for microcontrollers"), 0);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
wxString GetSelection() const { return m_choice->GetStringSelection(); }
|
||||
|
||||
private:
|
||||
wxChoice* m_choice;
|
||||
};
|
||||
|
||||
// Page 2: Pin Configuration
|
||||
class PinConfigPage : public wxWizardPageSimple
|
||||
{
|
||||
public:
|
||||
PinConfigPage(wxWizard* parent) : wxWizardPageSimple(parent)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 2: Pin Configuration"), 0, wxBOTTOM, 10);
|
||||
|
||||
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 2, 5, 10);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Number of Pins:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_pinCount = new wxSpinCtrl(this, wxID_ANY, "48", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 4, 256, 48);
|
||||
grid->Add(m_pinCount, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Pins Per Side:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_pinsPerSide = new wxSpinCtrl(this, wxID_ANY, "12", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 64, 12);
|
||||
grid->Add(m_pinsPerSide, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Pin Pitch (mm):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
wxString pitches[] = {"0.4", "0.5", "0.65", "0.8", "1.0", "1.27"};
|
||||
m_pitch = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6, pitches);
|
||||
m_pitch->SetSelection(1);
|
||||
grid->Add(m_pitch, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Pin 1 Position:"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
wxString positions[] = {"Top-Left", "Bottom-Left", "Top-Right", "Bottom-Right"};
|
||||
m_pin1Pos = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, positions);
|
||||
m_pin1Pos->SetSelection(0);
|
||||
grid->Add(m_pin1Pos, 0);
|
||||
|
||||
sizer->Add(grid, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
int GetPinCount() const { return m_pinCount->GetValue(); }
|
||||
wxString GetPitch() const { return m_pitch->GetStringSelection(); }
|
||||
|
||||
private:
|
||||
wxSpinCtrl* m_pinCount;
|
||||
wxSpinCtrl* m_pinsPerSide;
|
||||
wxChoice* m_pitch;
|
||||
wxChoice* m_pin1Pos;
|
||||
};
|
||||
|
||||
// Page 3: Package Dimensions
|
||||
class DimensionsPage : public wxWizardPageSimple
|
||||
{
|
||||
public:
|
||||
DimensionsPage(wxWizard* parent) : wxWizardPageSimple(parent)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 3: Package Dimensions"), 0, wxBOTTOM, 10);
|
||||
|
||||
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 2, 5, 10);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Package Width (mm):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_width = new wxTextCtrl(this, wxID_ANY, "7.0");
|
||||
grid->Add(m_width, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Package Height (mm):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_height = new wxTextCtrl(this, wxID_ANY, "7.0");
|
||||
grid->Add(m_height, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Pad Width (mm):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_padWidth = new wxTextCtrl(this, wxID_ANY, "0.3");
|
||||
grid->Add(m_padWidth, 0);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, "Pad Height (mm):"), 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_padHeight = new wxTextCtrl(this, wxID_ANY, "1.0");
|
||||
grid->Add(m_padHeight, 0);
|
||||
|
||||
sizer->Add(grid, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
// Checkbox options
|
||||
m_addThermal = new wxCheckBox(this, wxID_ANY, "Add thermal pad");
|
||||
m_addSilkscreen = new wxCheckBox(this, wxID_ANY, "Add silkscreen outline");
|
||||
m_addCourtyard = new wxCheckBox(this, wxID_ANY, "Add courtyard");
|
||||
|
||||
m_addSilkscreen->SetValue(true);
|
||||
m_addCourtyard->SetValue(true);
|
||||
|
||||
sizer->Add(m_addThermal, 0, wxBOTTOM, 5);
|
||||
sizer->Add(m_addSilkscreen, 0, wxBOTTOM, 5);
|
||||
sizer->Add(m_addCourtyard, 0);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
private:
|
||||
wxTextCtrl* m_width;
|
||||
wxTextCtrl* m_height;
|
||||
wxTextCtrl* m_padWidth;
|
||||
wxTextCtrl* m_padHeight;
|
||||
wxCheckBox* m_addThermal;
|
||||
wxCheckBox* m_addSilkscreen;
|
||||
wxCheckBox* m_addCourtyard;
|
||||
};
|
||||
|
||||
// Page 4: Summary
|
||||
class SummaryPage : public wxWizardPageSimple
|
||||
{
|
||||
public:
|
||||
SummaryPage(wxWizard* parent, PackageTypePage* p1, PinConfigPage* p2)
|
||||
: wxWizardPageSimple(parent), m_page1(p1), m_page2(p2)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Step 4: Summary"), 0, wxBOTTOM, 10);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Review your footprint settings:"), 0, wxBOTTOM, 10);
|
||||
|
||||
m_summary = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
sizer->Add(m_summary, 1, wxEXPAND | wxBOTTOM, 10);
|
||||
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, "Click Finish to create the footprint."), 0);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
virtual bool TransferDataToWindow() override
|
||||
{
|
||||
wxString summary;
|
||||
summary += "Footprint Summary\n";
|
||||
summary += "=================\n\n";
|
||||
summary += wxString::Format("Package Type: %s\n", m_page1->GetSelection());
|
||||
summary += wxString::Format("Pin Count: %d\n", m_page2->GetPinCount());
|
||||
summary += wxString::Format("Pin Pitch: %s mm\n", m_page2->GetPitch());
|
||||
summary += "\nDimensions:\n";
|
||||
summary += " Package: 7.0 x 7.0 mm\n";
|
||||
summary += " Pad Size: 0.3 x 1.0 mm\n";
|
||||
summary += "\nOptions:\n";
|
||||
summary += " Silkscreen: Yes\n";
|
||||
summary += " Courtyard: Yes\n";
|
||||
|
||||
m_summary->SetValue(summary);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
PackageTypePage* m_page1;
|
||||
PinConfigPage* m_page2;
|
||||
wxTextCtrl* m_summary;
|
||||
};
|
||||
|
||||
class WizardFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
WizardFrame() : wxFrame(nullptr, wxID_ANY, "wxWizard Test",
|
||||
wxDefaultPosition, wxSize(800, 500))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxWizard for the Footprint Wizard.\n"
|
||||
"This tests step-by-step dialog functionality with Next/Back navigation.");
|
||||
mainSizer->Add(desc, 0, wxALL, 10);
|
||||
|
||||
// Launch button
|
||||
wxButton* btnLaunch = new wxButton(mainPanel, wxID_ANY, "Launch Footprint Wizard");
|
||||
btnLaunch->Bind(wxEVT_BUTTON, &WizardFrame::OnLaunchWizard, this);
|
||||
mainSizer->Add(btnLaunch, 0, wxALL, 10);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 10);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
// Status bar
|
||||
CreateStatusBar();
|
||||
SetStatusText("Wizard test app started");
|
||||
|
||||
Log("Wizard test app started");
|
||||
Log("Click 'Launch Footprint Wizard' to start");
|
||||
}
|
||||
|
||||
private:
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
void OnLaunchWizard(wxCommandEvent& event)
|
||||
{
|
||||
Log("Launching Footprint Wizard...");
|
||||
|
||||
wxWizard* wizard = new wxWizard(this, wxID_ANY, "Footprint Wizard",
|
||||
wxNullBitmap, wxDefaultPosition,
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
|
||||
// Create pages
|
||||
PackageTypePage* page1 = new PackageTypePage(wizard);
|
||||
PinConfigPage* page2 = new PinConfigPage(wizard);
|
||||
DimensionsPage* page3 = new DimensionsPage(wizard);
|
||||
SummaryPage* page4 = new SummaryPage(wizard, page1, page2);
|
||||
|
||||
// Chain pages
|
||||
wxWizardPageSimple::Chain(page1, page2);
|
||||
wxWizardPageSimple::Chain(page2, page3);
|
||||
wxWizardPageSimple::Chain(page3, page4);
|
||||
|
||||
wizard->GetPageAreaSizer()->Add(page1);
|
||||
|
||||
// Bind wizard events
|
||||
wizard->Bind(wxEVT_WIZARD_PAGE_CHANGED, [this](wxWizardEvent& evt) {
|
||||
Log(wxString::Format("Page changed to: %d", evt.GetPage() ? 1 : 0));
|
||||
});
|
||||
|
||||
wizard->Bind(wxEVT_WIZARD_CANCEL, [this](wxWizardEvent&) {
|
||||
Log("Wizard cancelled");
|
||||
});
|
||||
|
||||
wizard->Bind(wxEVT_WIZARD_FINISHED, [this](wxWizardEvent&) {
|
||||
Log("Wizard finished - footprint would be created");
|
||||
});
|
||||
|
||||
if (wizard->RunWizard(page1))
|
||||
{
|
||||
Log("Footprint created successfully!");
|
||||
Log(wxString::Format(" Type: %s", page1->GetSelection()));
|
||||
Log(wxString::Format(" Pins: %d", page2->GetPinCount()));
|
||||
Log(wxString::Format(" Pitch: %s mm", page2->GetPitch()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Wizard was cancelled");
|
||||
}
|
||||
|
||||
wizard->Destroy();
|
||||
}
|
||||
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class WizardApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
WizardFrame* frame = new WizardFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(WizardApp);
|
||||
389
tests/apps/standalone/xml/xml_test.cpp
Normal file
389
tests/apps/standalone/xml/xml_test.cpp
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
// wxXmlDocument Test - XML parsing like KiCad's config and project files
|
||||
// Tests: wxXmlDocument, wxXmlNode, parsing, creation, traversal
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "wx/xml/xml.h"
|
||||
#include "wx/sstream.h"
|
||||
|
||||
class XmlFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
XmlFrame() : wxFrame(nullptr, wxID_ANY, "wxXmlDocument Test",
|
||||
wxDefaultPosition, wxSize(900, 700))
|
||||
{
|
||||
wxPanel* mainPanel = new wxPanel(this);
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Description
|
||||
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
|
||||
"KiCad uses wxXmlDocument for config/project files (665 occurrences).\n"
|
||||
"Tests: Parsing, node traversal, creation, modification, serialization.");
|
||||
mainSizer->Add(desc, 0, wxALL, 5);
|
||||
|
||||
// Sample XML
|
||||
wxStaticBoxSizer* sampleSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Sample XML (KiCad-like project)");
|
||||
|
||||
m_xmlInput = new wxTextCtrl(mainPanel, wxID_ANY, GetSampleXml(),
|
||||
wxDefaultPosition, wxSize(-1, 150),
|
||||
wxTE_MULTILINE | wxTE_DONTWRAP);
|
||||
m_xmlInput->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
sampleSizer->Add(m_xmlInput, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainSizer->Add(sampleSizer, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Buttons
|
||||
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* parseBtn = new wxButton(mainPanel, wxID_ANY, "Parse XML");
|
||||
parseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnParseXml, this);
|
||||
btnSizer->Add(parseBtn, 0, wxRIGHT, 5);
|
||||
|
||||
wxButton* traverseBtn = new wxButton(mainPanel, wxID_ANY, "Traverse Nodes");
|
||||
traverseBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnTraverseNodes, this);
|
||||
btnSizer->Add(traverseBtn, 0, wxRIGHT, 5);
|
||||
|
||||
wxButton* createBtn = new wxButton(mainPanel, wxID_ANY, "Create XML");
|
||||
createBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnCreateXml, this);
|
||||
btnSizer->Add(createBtn, 0, wxRIGHT, 5);
|
||||
|
||||
wxButton* modifyBtn = new wxButton(mainPanel, wxID_ANY, "Modify XML");
|
||||
modifyBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnModifyXml, this);
|
||||
btnSizer->Add(modifyBtn, 0, wxRIGHT, 5);
|
||||
|
||||
wxButton* serializeBtn = new wxButton(mainPanel, wxID_ANY, "Serialize");
|
||||
serializeBtn->Bind(wxEVT_BUTTON, &XmlFrame::OnSerializeXml, this);
|
||||
btnSizer->Add(serializeBtn, 0);
|
||||
|
||||
mainSizer->Add(btnSizer, 0, wxALL, 5);
|
||||
|
||||
// Results
|
||||
wxStaticBoxSizer* resultSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, "Results / Output");
|
||||
|
||||
m_output = new wxTextCtrl(mainPanel, wxID_ANY, "",
|
||||
wxDefaultPosition, wxSize(-1, 200),
|
||||
wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP);
|
||||
m_output->SetFont(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
|
||||
resultSizer->Add(m_output, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainSizer->Add(resultSizer, 1, wxEXPAND | wxALL, 5);
|
||||
|
||||
// Event log
|
||||
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
|
||||
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 80),
|
||||
wxTE_MULTILINE | wxTE_READONLY);
|
||||
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
mainPanel->SetSizer(mainSizer);
|
||||
|
||||
CreateStatusBar();
|
||||
SetStatusText("XML test app started");
|
||||
Log("XML test app started");
|
||||
}
|
||||
|
||||
private:
|
||||
wxString GetSampleXml()
|
||||
{
|
||||
return
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<kicad_project version=\"1\">\n"
|
||||
" <general>\n"
|
||||
" <name>MyProject</name>\n"
|
||||
" <version>8.0</version>\n"
|
||||
" </general>\n"
|
||||
" <schematic>\n"
|
||||
" <drawing>\n"
|
||||
" <sheet name=\"Root\" page=\"A4\"/>\n"
|
||||
" </drawing>\n"
|
||||
" </schematic>\n"
|
||||
" <pcb>\n"
|
||||
" <layers count=\"4\">\n"
|
||||
" <layer id=\"0\" name=\"F.Cu\" type=\"copper\"/>\n"
|
||||
" <layer id=\"1\" name=\"In1.Cu\" type=\"copper\"/>\n"
|
||||
" <layer id=\"2\" name=\"In2.Cu\" type=\"copper\"/>\n"
|
||||
" <layer id=\"31\" name=\"B.Cu\" type=\"copper\"/>\n"
|
||||
" </layers>\n"
|
||||
" <design_rules>\n"
|
||||
" <track_width min=\"0.2\" default=\"0.25\"/>\n"
|
||||
" <via_size min=\"0.4\" default=\"0.8\"/>\n"
|
||||
" </design_rules>\n"
|
||||
" </pcb>\n"
|
||||
"</kicad_project>\n";
|
||||
}
|
||||
|
||||
void OnParseXml(wxCommandEvent& event)
|
||||
{
|
||||
Log("Parsing XML...");
|
||||
m_output->Clear();
|
||||
|
||||
wxString xmlStr = m_xmlInput->GetValue();
|
||||
wxStringInputStream stream(xmlStr);
|
||||
|
||||
wxXmlDocument doc;
|
||||
if (!doc.Load(stream))
|
||||
{
|
||||
Output("ERROR: Failed to parse XML");
|
||||
Log("Parse failed");
|
||||
return;
|
||||
}
|
||||
|
||||
wxXmlNode* root = doc.GetRoot();
|
||||
if (!root)
|
||||
{
|
||||
Output("ERROR: No root element");
|
||||
return;
|
||||
}
|
||||
|
||||
Output(wxString::Format("Parse successful!\n"));
|
||||
Output(wxString::Format("Root element: <%s>\n", root->GetName()));
|
||||
Output(wxString::Format("Version attribute: %s\n", root->GetAttribute("version", "none")));
|
||||
|
||||
// Count children
|
||||
int childCount = 0;
|
||||
wxXmlNode* child = root->GetChildren();
|
||||
while (child)
|
||||
{
|
||||
if (child->GetType() == wxXML_ELEMENT_NODE)
|
||||
childCount++;
|
||||
child = child->GetNext();
|
||||
}
|
||||
Output(wxString::Format("Child elements: %d\n", childCount));
|
||||
|
||||
Log("Parse complete");
|
||||
}
|
||||
|
||||
void OnTraverseNodes(wxCommandEvent& event)
|
||||
{
|
||||
Log("Traversing XML nodes...");
|
||||
m_output->Clear();
|
||||
|
||||
wxString xmlStr = m_xmlInput->GetValue();
|
||||
wxStringInputStream stream(xmlStr);
|
||||
|
||||
wxXmlDocument doc;
|
||||
if (!doc.Load(stream))
|
||||
{
|
||||
Output("ERROR: Failed to parse XML");
|
||||
return;
|
||||
}
|
||||
|
||||
wxXmlNode* root = doc.GetRoot();
|
||||
TraverseNode(root, 0);
|
||||
|
||||
Log("Traversal complete");
|
||||
}
|
||||
|
||||
void TraverseNode(wxXmlNode* node, int depth)
|
||||
{
|
||||
if (!node) return;
|
||||
|
||||
wxString indent(depth * 2, ' ');
|
||||
|
||||
if (node->GetType() == wxXML_ELEMENT_NODE)
|
||||
{
|
||||
wxString attrs;
|
||||
wxXmlAttribute* attr = node->GetAttributes();
|
||||
while (attr)
|
||||
{
|
||||
attrs += wxString::Format(" %s=\"%s\"", attr->GetName(), attr->GetValue());
|
||||
attr = attr->GetNext();
|
||||
}
|
||||
|
||||
wxString content = node->GetNodeContent().Trim();
|
||||
if (!content.IsEmpty())
|
||||
{
|
||||
Output(wxString::Format("%s<%s%s>%s</%s>\n",
|
||||
indent, node->GetName(), attrs, content, node->GetName()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Output(wxString::Format("%s<%s%s>\n", indent, node->GetName(), attrs));
|
||||
|
||||
// Traverse children
|
||||
wxXmlNode* child = node->GetChildren();
|
||||
while (child)
|
||||
{
|
||||
TraverseNode(child, depth + 1);
|
||||
child = child->GetNext();
|
||||
}
|
||||
|
||||
if (node->GetChildren())
|
||||
Output(wxString::Format("%s</%s>\n", indent, node->GetName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnCreateXml(wxCommandEvent& event)
|
||||
{
|
||||
Log("Creating new XML document...");
|
||||
m_output->Clear();
|
||||
|
||||
// Create a new document
|
||||
wxXmlDocument doc;
|
||||
doc.SetVersion("1.0");
|
||||
doc.SetFileEncoding("UTF-8");
|
||||
|
||||
// Create root element
|
||||
wxXmlNode* root = new wxXmlNode(wxXML_ELEMENT_NODE, "component");
|
||||
root->AddAttribute("type", "resistor");
|
||||
doc.SetRoot(root);
|
||||
|
||||
// Add child elements
|
||||
wxXmlNode* refNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "reference");
|
||||
refNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "R1"));
|
||||
|
||||
wxXmlNode* valueNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "value");
|
||||
valueNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "10k"));
|
||||
|
||||
wxXmlNode* footprintNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "footprint");
|
||||
footprintNode->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "Resistor_SMD:R_0402"));
|
||||
|
||||
// Properties
|
||||
wxXmlNode* propsNode = new wxXmlNode(root, wxXML_ELEMENT_NODE, "properties");
|
||||
|
||||
wxXmlNode* prop1 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
|
||||
prop1->AddAttribute("name", "tolerance");
|
||||
prop1->AddAttribute("value", "1%");
|
||||
|
||||
wxXmlNode* prop2 = new wxXmlNode(propsNode, wxXML_ELEMENT_NODE, "property");
|
||||
prop2->AddAttribute("name", "power");
|
||||
prop2->AddAttribute("value", "0.1W");
|
||||
|
||||
// Serialize to string
|
||||
wxStringOutputStream outStream;
|
||||
if (doc.Save(outStream))
|
||||
{
|
||||
Output("Created XML document:\n\n");
|
||||
Output(outStream.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("ERROR: Failed to serialize");
|
||||
}
|
||||
|
||||
Log("XML creation complete");
|
||||
}
|
||||
|
||||
void OnModifyXml(wxCommandEvent& event)
|
||||
{
|
||||
Log("Modifying XML...");
|
||||
m_output->Clear();
|
||||
|
||||
wxString xmlStr = m_xmlInput->GetValue();
|
||||
wxStringInputStream stream(xmlStr);
|
||||
|
||||
wxXmlDocument doc;
|
||||
if (!doc.Load(stream))
|
||||
{
|
||||
Output("ERROR: Failed to parse XML");
|
||||
return;
|
||||
}
|
||||
|
||||
wxXmlNode* root = doc.GetRoot();
|
||||
|
||||
// Find and modify the general/name element
|
||||
wxXmlNode* general = FindChild(root, "general");
|
||||
if (general)
|
||||
{
|
||||
wxXmlNode* name = FindChild(general, "name");
|
||||
if (name && name->GetChildren())
|
||||
{
|
||||
wxString oldName = name->GetNodeContent();
|
||||
name->GetChildren()->SetContent("ModifiedProject");
|
||||
Output(wxString::Format("Changed project name: '%s' -> 'ModifiedProject'\n", oldName));
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new element to pcb
|
||||
wxXmlNode* pcb = FindChild(root, "pcb");
|
||||
if (pcb)
|
||||
{
|
||||
wxXmlNode* newElem = new wxXmlNode(wxXML_ELEMENT_NODE, "modified");
|
||||
newElem->AddAttribute("timestamp", wxDateTime::Now().FormatISOCombined());
|
||||
pcb->AddChild(newElem);
|
||||
Output("Added <modified> element to <pcb>\n");
|
||||
}
|
||||
|
||||
// Serialize modified document
|
||||
wxStringOutputStream outStream;
|
||||
if (doc.Save(outStream))
|
||||
{
|
||||
Output("\nModified XML:\n");
|
||||
Output(outStream.GetString());
|
||||
}
|
||||
|
||||
Log("Modification complete");
|
||||
}
|
||||
|
||||
void OnSerializeXml(wxCommandEvent& event)
|
||||
{
|
||||
Log("Serializing XML...");
|
||||
m_output->Clear();
|
||||
|
||||
wxString xmlStr = m_xmlInput->GetValue();
|
||||
wxStringInputStream inStream(xmlStr);
|
||||
|
||||
wxXmlDocument doc;
|
||||
if (!doc.Load(inStream))
|
||||
{
|
||||
Output("ERROR: Failed to parse XML");
|
||||
return;
|
||||
}
|
||||
|
||||
// Serialize with formatting
|
||||
wxStringOutputStream outStream;
|
||||
if (doc.Save(outStream, 2)) // Indent with 2 spaces
|
||||
{
|
||||
Output("Serialized XML (formatted):\n\n");
|
||||
Output(outStream.GetString());
|
||||
|
||||
// Show statistics
|
||||
wxString serialized = outStream.GetString();
|
||||
Output(wxString::Format("\n--- Statistics ---\n"));
|
||||
Output(wxString::Format("Total size: %zu bytes\n", serialized.Length()));
|
||||
Output(wxString::Format("Lines: %d\n", serialized.Freq('\n') + 1));
|
||||
}
|
||||
|
||||
Log("Serialization complete");
|
||||
}
|
||||
|
||||
wxXmlNode* FindChild(wxXmlNode* parent, const wxString& name)
|
||||
{
|
||||
if (!parent) return nullptr;
|
||||
|
||||
wxXmlNode* child = parent->GetChildren();
|
||||
while (child)
|
||||
{
|
||||
if (child->GetType() == wxXML_ELEMENT_NODE && child->GetName() == name)
|
||||
return child;
|
||||
child = child->GetNext();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Output(const wxString& text)
|
||||
{
|
||||
m_output->AppendText(text);
|
||||
}
|
||||
|
||||
void Log(const wxString& msg)
|
||||
{
|
||||
m_log->AppendText(msg + "\n");
|
||||
}
|
||||
|
||||
wxTextCtrl* m_xmlInput;
|
||||
wxTextCtrl* m_output;
|
||||
wxTextCtrl* m_log;
|
||||
};
|
||||
|
||||
class XmlApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit() override
|
||||
{
|
||||
XmlFrame* frame = new XmlFrame();
|
||||
frame->Show();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
wxIMPLEMENT_APP(XmlApp);
|
||||
Loading…
Reference in a new issue