Add wxDataViewCtrl, wxHtmlWindow, and wxStyledTextCtrl test apps

Test 3 previously untested KiCad-critical wxWidgets controls:
- wxDataViewCtrl: List/tree views for Zone Manager, Net Inspector (10 tests)
- wxHtmlWindow: HTML rendering for About dialogs, error formatting (8 tests)
- wxStyledTextCtrl: Syntax highlighting for DRC rules, Python console (10 tests)

Also add button-finder config and update docs. All 119 tests passing.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-12-03 11:27:00 +01:00
commit 9a78db1528
42 changed files with 1900 additions and 17 deletions

View file

@ -17,6 +17,12 @@ 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)
# Debug or Release build
ifdef DEBUG
# Debug: DWARF info, source maps, minimal optimization
@ -49,6 +55,12 @@ LDFLAGS_GL = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(EM_GL_FLAGS) --js-library=$(GL_S
# 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)
JS = $(TOOLS_ROOT)/wx.js
HTML = $(TOOLS_ROOT)/template.html
@ -65,7 +77,10 @@ all: minimal_test.html \
$(S)/grid/grid_test.html \
$(S)/dialog/dialog_test.html \
$(S)/timer/timer_test.html \
$(S)/tree/tree_test.html
$(S)/tree/tree_test.html \
$(S)/dataview/dataview_test.html \
$(S)/htmlwin/htmlwin_test.html \
$(S)/stc/stc_test.html
# Main test app (uses GL)
minimal_test.o: minimal_test.cpp
@ -144,6 +159,27 @@ $(S)/tree/tree_test.o: $(S)/tree/tree_test.cpp
$(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 $@
# Convenience targets
menu: $(S)/menu/menu_test.html
clipboard: $(S)/clipboard/clipboard_test.html
@ -155,9 +191,12 @@ 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
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
.PHONY: all clean menu clipboard filedialog layout aui toolbar grid dialog timer tree dataview htmlwin stc

View file

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

View file

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

View file

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