// 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
#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");
}