pcbjam/tests/apps/standalone/ownerdrawn/ownerdrawn_test.cpp
Viktor Vaczi a26b40c063 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>
2025-12-27 14:06:23 +01:00

330 lines
12 KiB
C++

// 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);