UIng

Screenshot Tests Lines of Code Ask DeepWiki

CI • Linux (x64) CI • Linux (ARM64) CI • macOS (ARM64) CI • macOS (Intel) CI • Windows (x64, MSVC) CI • Windows (x64, MinGW64) CI • Windows (x64, UCRT64)

UIng is a Crystal binding for kojix2/libui-ng. You can use the Crystal language to create cross-platform native desktop apps.

libui-ng uses the native APIs of each platform: Win32 API, Direct2D, and DirectWrite on Windows; Cocoa (AppKit) on macOS; and GTK+ 3.10+ and Pango on Linux/Unix. You get windows, buttons, text boxes, menus, dialogs, drawing areas, and other standard widgets.

Windows Mac Linux

📸 Live Documentation: All screenshots in the README are automatically generated by GitHub Actions on every push, ensuring cross-platform compatibility (Linux, Windows, macOS).

Supported Platforms

Installation

Add the dependency to your shard.yml:

dependencies:
  uing:
    github: kojix2/uing

Quick Start

Clone the repository:

git clone https://github.com/kojix2/uing
cd uing

Create the libui directory and download the static library for your platform:

crystal run download.cr

To run the control_gallery example, use the following command:

crystal run examples/gallery/control_gallery.cr

Windows MSVC Setup

The MSVC build requires Visual Studio Build Tools and a Windows SDK. Run the commands above in x64 Native Tools Command Prompt or Developer PowerShell for Visual Studio, because a regular Command Prompt or PowerShell session is not configured for MSVC.

Usage

require "uing"

UIng.init

window = UIng::Window.new("Hello World", 300, 200)
window.on_closing do
  UIng.quit
  true
end

button = UIng::Button.new("Click me")
button.on_clicked do
  window.msg_box("Info", "Button clicked!")
end

window.set_child(button)
window.show

UIng.main
UIng.uninit

DSL style

require "uing"

UIng.init do
  UIng::Window.new("Hello World", 300, 200) { |win|
    on_closing { UIng.quit; true }
    set_child {
      UIng::Button.new("Click me") {
        on_clicked {
          win.msg_box("Info", "Button clicked!")
        }
      }
    }
    show
  }

  UIng.main
end

Note: The DSL style is implemented using Crystal's with ... yield syntax internally.

Examples Gallery

This gallery shows screenshots of example on three platforms (Ubuntu, Windows, macOS).
Images are automatically generated and stored in the screenshots branch.

Window

Control Ubuntu Windows macOS
Window basic_window-ubuntu basic_window-windows basic_window-macos
Toolbar basic_toolbar-ubuntu basic_toolbar-windows basic_toolbar-macos

Note: Toolbar is a feature specific to kojix2/libui-ng. It is experimental and may change.

Control

Control Ubuntu Windows macOS
Button basic_button-ubuntu basic_button-windows basic_button-macos
Checkbox basic_checkbox-ubuntu basic_checkbox-windows basic_checkbox-macos
ColorButton basic_color_button-ubuntu basic_color_button-windows basic_color_button-macos
Combobox basic_combobox-ubuntu basic_combobox-windows basic_combobox-macos
DateTimePicker basic_date_time_picker-ubuntu basic_date_time_picker-windows basic_date_time_picker-macos
EditableCombobox basic_editable_combobox-ubuntu basic_editable_combobox-windows basic_editable_combobox-macos
Entry basic_entry-ubuntu basic_entry-windows basic_entry-macos
FontButton basic_font_button-ubuntu basic_font_button-windows basic_font_button-macos
Label basic_label-ubuntu basic_label-windows basic_label-macos
MultilineEntry basic_multiline_entry-ubuntu basic_multiline_entry-windows basic_multiline_entry-macos
Progressbar basic_progressbar-ubuntu basic_progressbar-windows basic_progressbar-macos
RadioButtons basic_radio_buttons-ubuntu basic_radio_buttons-windows basic_radio_buttons-macos
Separator basic_separator-ubuntu basic_separator-windows basic_separator-macos
Slider basic_slider-ubuntu basic_slider-windows basic_slider-macos
Spinbox basic_spinbox-ubuntu basic_spinbox-windows basic_spinbox-macos

Container Control

Container Ubuntu Windows macOS
Box (Horizontal) basic_box_horizontal-ubuntu basic_box_horizontal-windows basic_box_horizontal-macos
Box (Vertical) basic_box_vertical-ubuntu basic_box_vertical-windows basic_box_vertical-macos
Tab basic_tab-ubuntu basic_tab-windows basic_tab-macos
Form basic_form-ubuntu basic_form-windows basic_form-macos
Group basic_group-ubuntu basic_group-windows basic_group-macos
Grid basic_grid-ubuntu basic_grid-windows basic_grid-macos
Grid (Calculator) grid_calculator-ubuntu grid_calculator-windows grid_calculator-macos

Table

Example Ubuntu Windows macOS
basic_table basic_table-ubuntu basic_table-windows basic_table-macos
csv_viewer csv_viewer-ubuntu csv_viewer-windows csv_viewer-macos
advanced_table advanced_table-ubuntu advanced_table-windows advanced_table-macos

Area

Example Ubuntu Windows macOS
basic_area basic_area-ubuntu basic_area-windows basic_area-macos
area_basic_shapes area_basic_shapes-ubuntu area_basic_shapes-windows area_basic_shapes-macos
area_colors_and_brushes area_colors_and_brushes-ubuntu area_colors_and_brushes-windows area_colors_and_brushes-macos
area_analog_clock area_analog_clock-ubuntu area_analog_clock-windows area_analog_clock-macos
spirograph spirograph-ubuntu spirograph-windows spirograph-macos
area_matrix area_matrix-ubuntu area_matrix-windows area_matrix-macos
basic_draw_text basic_draw_text-ubuntu basic_draw_text-windows basic_draw_text-macos
reversi reversi-ubuntu reversi-windows reversi-macos
area_breakout area_breakout-ubuntu area_breakout-windows area_breakout-macos
boid3d boid3d-ubuntu boid3d-windows boid3d-macos

Menu

Example Ubuntu Windows macOS
basic_menu basic_menu-ubuntu basic_menu-windows basic_menu-macos

Dialog

Example Ubuntu Windows macOS
basic_msg_box basic_msg_box-ubuntu basic_msg_box-windows basic_msg_box-macos
basic_msg_box_error basic_msg_box_error-ubuntu basic_msg_box_error-windows basic_msg_box_error-macos

Image

Example Ubuntu Windows macOS
area_draw_image area_draw_image-ubuntu area_draw_image-windows area_draw_image-macos
basic_image_view basic_image_view-ubuntu basic_image_view-windows basic_image_view-macos

Note: Image display is a feature introduced in kojix2/libui-ng. This feature is not present in the original libui-ng.

API Levels

Level Defined in Example Description
High-Level src/uing/*.cr button.on_clicked { }, etc. Object-oriented API
Low-Level src/uing/lib_ui/lib_ui.cr UIng::LibUI.new_button, etc. Direct bindings to libui

Memory Management Policy

Some UIng objects must be cleaned up manually when they are no longer needed. Use destroy for controls and free for some other resources, such as images.

This section explains when and how to clean up UIng objects.

Parent and Child Controls

Some UIng controls can contain other controls. For example, a Window can contain a Box, and a Box can contain controls such as Button. The containing control is the parent, and a control inside it is a child.

Destroying a parent automatically destroys all of its children. Normally, you only need to destroy the parent rather than each child individually.

window = UIng::Window.new("App", 400, 300)
box = UIng::Box.new(:vertical)
button = UIng::Button.new("OK")

box.append(button)
window.child = box

window.destroy # also destroys box and button

UIng also marks wrappers for those children as destroyed, so they can no longer be used.

To reuse a child elsewhere, detach it before destroying its parent:

button.detach # still alive
other_box.append(button)

To destroy a child individually, detach it from its parent first and then call destroy:

button.detach
button.destroy

Calling destroy on a child that is still attached raises an exception and leaves the child intact.

The following methods add, remove, or replace children in each type of parent:

Closing an Application or Window

Most controls are placed under a top-level window. Destroying that window automatically destroys its children, so you do not need to call destroy on each control individually.

UIng.quit stops the event loop; it does not destroy windows. UIng.uninit shuts down the UI system and cleans up its internal resources, but it does not destroy windows created by the application. Destroy all top-level windows before calling UIng.uninit.

Window#on_closing is called mainly when the user clicks a window's close button. For a simple application with one window, this callback is usually enough to handle shutdown.

Return true from Window#on_closing to close and destroy the window, or false to keep it open. When the callback returns true, libui-ng destroys the window. The callback therefore only needs to call UIng.quit and return true:

window.on_closing do
  UIng.quit
  true
end

There is no need to call window.destroy as well on this exit path.

UIng.on_should_quit handles requests to quit the entire application, such as choosing Quit from a menu. It is separate from Window#on_closing, which handles a window's close button.

When exiting from UIng.on_should_quit, destroy every top-level window created by the application. The callback does not destroy them automatically.

When using both callbacks, follow this pattern:

window.on_closing do
  UIng.quit
  true
end

UIng.on_should_quit do
  window.destroy unless window.released?
  true
end

Cleanup Rules for Other Objects

For objects that are not controls, the cleanup method depends on how the object was obtained:

The following rules apply to specific objects:

Calling destroy or free makes the corresponding wrapper unavailable for further use.

Limitations

libui-ng is cross-platform, but comes with some limitations:

  1. Precise widget positioning is not possible. Control placement is intentionally coarse and cannot be specified numerically. This is an intentional constraint to ensure consistent behavior across all three platforms.

  2. There is no function to delete columns from the table.

Windows Setup

Hide Console Window

MinGW:

crystal build app.cr --link-flags "-mwindows"

MSVC:

crystal build app.cr --link-flags=/SUBSYSTEM:WINDOWS

Packaging Your Application

To learn how to package your UIng-based application for distribution, refer to the md5_checker example.
This example demonstrates a simple way to bundle your Crystal app with the required native libraries, making it easy to share with others.

Development

Purpose

This project aims to provide a small, sustainable foundation for building simple native GUIs.

Our priority is not to keep adding new features, but to keep the library working, stable, and maintainable over the long term. Providing a full-featured GUI library is not the main scope of this project.

UIng::LibUI

Memory Safety

UIng applies several strategies to ensure safe interoperation between Crystal’s garbage-collected runtime and native C code:

Closures in Low-Level Contexts

Use of Generative AI

This project is developed with the assistance of generative AI.

AI is used extensively for:

UIng was initially built through manual work, iterative design, and line-by-line human review of AI-generated code. In 2026, line-by-line review was discontinued as AI's ability to detect bugs surpassed kojix2's. Human effort now focuses on the project's overall design, visual inspection of the GUI, and finding improvements through real-world use of UIng.

Contributing

License

MIT License