Skip to main content


What are widgets?

Widgets in Capy are represented in two ways due to lack of OOP:

  • The actual widget (Button, TextArea...) , which we will now call the component and which is first stored in the stack (with your window.set call) and then stored in heap when properly initialised.
  • The generic widget, which we will now call the widget, is represented by the capy.Widget struct which contains a pointer to the component's class and data and some other properties.


To check if a widget is backing a given component, the following code can be used:

if ( { // is the widget representing a button?
// ...

Similarly, if you're sure a widget represents a button, you can do

const button: *Button_Impl =;

And finally,

if ( {
const button =;
// ...

can be shortened to

if (widget.cast(Button_Impl)) |button| {
// ...


Each component shares the following functions:

fn getWidth() u32Returns the width of the component, in pixels
fn getHeight() u32Returns the height of the component, in pixels
fn asWidget() anyerror!WidgetReturns a new widget linked to a copy of the component, if it is known that the component already has a widget, then error.ComponentAlreadyHasWidget is returned
fn setUserdata(userdata: ?*anyopaque)Sets the userdata of the component to the given pointer (all pointers automatically cast to ?*anyopaque) for later retrieval
fn getUserdata(comptime T: type)Returns the userdata as a pointer of type T (e.g. *u32 or *const Widget)
fn getWidget() ?*WidgetIf the component is associated with a (generic) Widget, returns it
fn getParent() ?*Container_ImplReturns the direct parent of this component, or null if it doesn't have one. The result is Container_Impl and not Widget as only a Container can be the parent of other components.
fn getRoot() ?*Container_ImplGoes up the widget tree (by taking the parent) until we're on a widget with no parent, that is, the root, and returns it. The widget tree's root is usually the one that was set with window.set(). Returns null if the component is unparented.

Each component also shares the following properties (alongside their respective get, set and bind functions):

opacity: f64The opacity of the component from 0 to 1. Thus, 0 means the component is fully transparent while 1 means it is opaque.
name: ?[]const u8The name of the widget. It is generally used for the Container.get() method. Note it doesn't have a bindName method


Every component can also have multiple handlers for different kinds of events.

(T is the type of the component)

NameCallback typeDescription
addClickHandlerfn (widget: *T) anyerror!voidCalled on button click (only for Button)
addDrawHandlerfn (widget: *T, ctx: *Canvas_Impl.DrawContext) anyerror!voidCalled when needing to draw (only for Canvas)
addMouseButtonHandlerfn (widget: *T, button: MouseButton, pressed: bool, x: u32, y: u32) anyerror!voidCalled when a mouse button is pressed (pressed = true) or released (pressed = false)
addMouseMotionHandlerfn (widget: *T, x: u32, y: u32) anyerror!voidCalled when the mouse is moved
addScrollHandlerfn (widget: *T, dx: f32, dy: f32) anyerror!voidCalled when the mouse wheel has moved, indicating scroll. Unit of dx and dy is arbitrary but usually is in number of 'ticks' or 'lines' scrolled
addResizeHandlerfn (widget: *T, size: Size) anyerror!voidCalled when the component is resized
addKeyTypeHandlerfn (widget: *T, key: []const u8) anyerror!voidCalled when a character is pressed. key is the UTF-8 encoding of the character