pcbjam/tests/apps/standalone/popup/popup_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

364 lines
12 KiB
C++

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