Skip to content

Latest commit

 

History

History
195 lines (148 loc) · 7.49 KB

File metadata and controls

195 lines (148 loc) · 7.49 KB

kwxTypeScript

TypeScript language bindings for wxWidgets via kwxFFI, enabling cross-platform GUI applications that render with native controls on Windows, macOS, and Linux.

⚠️ Warning: Not Production Ready

Do not use this TypeScript/wxWidgets interface in production.

  • The kwxFFI ABI interface is not stable and can change without warning.
  • Very little testing has been done, and won't be until late Q3 of 2026.
  • API surface may change as idioms are refined.

Requirements

  • Deno — the modern JavaScript/TypeScript runtime with built-in FFI support. Install from deno.com or the GitHub repository.
  • CMake 3.30+ (for building the kwxFFI shared library)
  • A supported C/C++ compiler (MSVC, MinGW, GCC, or Clang)

Overview

kwxTypeScript provides a two-layer binding architecture:

  1. FFI Layer (wx/*_gen.ts) — Auto-generated by kwxgen. TypeScript classes wrapping Deno.dlopen() FFI symbols with Deno.UnsafePointer operations mapping directly to kwxFFI C function exports. One file per wxWidgets class.
  2. Wrapper Barrel (wx/kwx_gen.ts) — Master barrel re-export of all generated bindings, constants, and the shared library handle. Users import individual classes or constants directly from here.

Users work with generated classes directly via imports. The kwxApp module (src/kwxApp.ts) provides the application lifecycle layer (initialize, main loop, shutdown).

TypeScript code  →  wx/*_gen.ts (FFI declarations)  →  kwxFFI (C functions)  →  wxWidgets (C++)

Quick Example

// examples/demo.ts - Hello World wxWidgets application in TypeScript

import {
  ALL, ALIGN_CENTER_VERTICAL, BOTH, DEFAULT_FRAME_STYLE,
  EVT_BUTTON, EVT_CLOSE_WINDOW, EVT_MENU, EXPAND,
  HORIZONTAL, ICON_INFORMATION, ID_ABOUT, ID_ANY, ID_EXIT,
  OK, VERTICAL,
} from "../wx/kwx_constants_gen.ts";
import { wxBoxSizer } from "../wx/wxBoxSizer_gen.ts";
import { wxButton } from "../wx/wxButton_gen.ts";
import { wxClosure } from "../wx/wxClosure_gen.ts";
import { wxFrame } from "../wx/wxFrame_gen.ts";
import { wxMenu, wxMenuBar } from "../wx/wxMenu_gen.ts";
import { wxMessageDialog } from "../wx/wxMessageDialog_gen.ts";
import { wxPanel } from "../wx/wxPanel_gen.ts";
import { wxStaticText } from "../wx/wxStaticText_gen.ts";
import { wxString } from "../wx/wxString_gen.ts";
import { lib } from "../wx/kwx_ffi_gen.ts";
import * as App from "../src/kwxApp.ts";

// Initialize wxWidgets
if (!App.initialize()) { Deno.exit(1); }
App.setAppName("kwxTypeScript Demo");

// Create main frame
const title = createWxString("kwxTypeScript Demo");
const frame = wxFrame.Create(null, ID_ANY, title.ptr, -1, -1, 480, 320, DEFAULT_FRAME_STYLE)!;
title.Delete();

// Panel + sizers
const panel = wxPanel.Create(frame.ptr, ID_ANY, 0, 0, 0, 0, 0)!;
const sizer = wxBoxSizer.Create(VERTICAL)!;

const label = wxStaticText.Create(panel.ptr, ID_ANY,
  createWxString("Welcome to kwxTypeScript!").ptr, -1, -1, -1, -1, 0)!;
const button = wxButton.Create(panel.ptr, ID_ANY,
  createWxString("Click Me").ptr, -1, -1, -1, -1, 0)!;

// Connect button click event
const cb = new Deno.UnsafeCallback({
  parameters: ["pointer", "pointer", "pointer"], result: "void",
}, () => {
  // ... message box logic
});
const closure = wxClosure.Create(cb.pointer, null)!;
lib.symbols.wxEvtHandler_Connect(button.ptr, ID_ANY, ID_ANY, EVT_BUTTON, closure.ptr);

sizer.AddWindow(label.ptr, 0, ALL, 10, null);
sizer.AddWindow(button.ptr, 0, ALL, 10, null);

frame.Show(true);
frame.Center(BOTH);
App.setTopWindow(frame.ptr);
Deno.exit(App.run());

A complete runnable example is in examples/demo.ts.

Run it with:

deno run --allow-ffi --allow-read --allow-env examples/demo.ts

Module Structure

Generated TypeScript classes wrap individual C++ wxWidgets classes via FFI. Inheritance is flattened — each generated class owns its own FFI symbols for the methods it declares, and users compose inherited behavior via helper shims or direct FFI calls on the parent class's symbol set.

wxEvtHandler
└── wxWindow
    ├── wxTopLevelWindow
    │   ├── wxFrame
    │   └── wxDialog
    ├── wxPanel
    ├── wxButton
    ├── wxStaticText
    ├── wxTextCtrl
    ├── wxCheckBox
    ├── wxChoice
    ├── wxListBox
    └── wxComboBox

wxSizer
├── wxBoxSizer
├── wxFlexGridSizer
└── wxGridSizer

All objects are created through generated ClassName.Create(...) static methods which return wrapper objects holding a Deno.PointerValue (opaque void*).

Naming Conventions

Entity Convention Example
Generated modules <classname>_gen.ts wxframe_gen.ts, wxbutton_gen.ts
Barrel re-export kwx_gen.ts import { wxButton } from "./wx/kwx_gen.ts"
Constructors ClassName.Create(...) wxFrame.Create(...), wxButton.Create(...)
Methods PascalCase .Show(), .Close(), .CreateStatusBar()
Constants UPPER_CASE EXPAND, DEFAULT_FRAME_STYLE, ID_ANY
Event types EVT_* EVT_BUTTON, EVT_CLOSE_WINDOW, EVT_MENU
App lifecycle kwxApp.ts App.initialize(), App.run(), App.exit()
Raw FFI symbols lib.symbols.wx* lib.symbols.wxWindow_Show(...)

Event Handling

Events are connected using Deno.UnsafeCallback wrapped in a wxClosure:

// Connect a callback to a button click
const cb = new Deno.UnsafeCallback({
  parameters: ["pointer", "pointer", "pointer"],
  result: "void",
}, (closureFun, data, event) => {
  console.log("clicked");
});

const closure = wxClosure.Create(cb.pointer, null)!;
lib.symbols.wxEvtHandler_Connect(button.ptr, BTN_ID, BTN_ID, EVT_BUTTON, closure.ptr);

// IMPORTANT: Keep references to UnsafeCallback instances alive —
// if GC'd, the native function pointer becomes dangling and causes a crash.

The wxClosure object wraps a raw Deno.UnsafeCallback.pointer so that it can be passed through the C ABI. Event type constants (EVT_BUTTON, EVT_CLOSE_WINDOW, etc.) are plain integers exported from kwx_constants_gen.ts.

Why TypeScript + wxWidgets?

TypeScript is one of the most widely adopted languages for application development, but native cross-platform GUI has traditionally required C++ or heavy framework dependencies. kwxTypeScript combines Deno's built-in FFI (Deno.dlopen) with wxWidgets' native rendering: one small shared library, native look on every supported platform, no separate runtime installation beyond Deno itself.

Compilers & Toolchain

Toolchain Status
MSVC cl.exe (Windows) ✅ Primary
MinGW (Windows) ✅ Supported
GCC (Linux / macOS) 🔧 Planned

The kwxFFI shared library is built automatically from source by CMake via FetchContent — including wxWidgets itself.

Building

Prerequisites:

  • Deno 1.x or later
  • CMake 3.30+
  • A supported C/C++ compiler (see above)
  • Internet access for FetchContent (kwxFFI and wxWidgets are fetched automatically)
# Configure and build (Windows, MSVC)
cmake --preset windows-msvc
cmake --build build --config Debug
# Copy the shared library to lib/ (done automatically by CMake)
# Run the demo
copy build\lib\Debug\kwxFFI.dll .
deno run --allow-ffi --allow-read --allow-env examples/demo.ts

License

Apache License 2.0 — see LICENSE for details.