Skip to content

Hammad-hab/Mojo-GTK

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

42 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Mojo-GTK

Auto-generated GTK4 bindings for Mojo, enabling you to build native GUI applications with modern Mojo syntax.

πŸ“‹ Table of Contents

🎯 Overview

Mojo-GTK provides comprehensive bindings for GTK4, allowing Mojo developers to create cross-platform desktop applications with native look and feel. The bindings are automatically generated from GTK's introspection data, ensuring accuracy and completeness.

πŸ“¦ Installation

Prerequisites

  1. Install GTK4 on your system:

    macOS:

    brew install gtk4

    Ubuntu/Debian:

    sudo apt-get install libgtk-4-dev

    Fedora:

    sudo dnf install gtk4-devel
  2. Install Mojo from modular.com

Setup

Clone the repository:

   git clone https://github.com/Hammad-hab/Mojo-GTK.git
   cd Mojo-GTK

Or download gtk.mojo which contains all the definations

  1. Configure library paths in pixi.toml:

    [activation.env]
    MODULAR_MOJO_MAX_SYSTEM_LIBS = "path/to/gtk/libs"

    You can find the required paths using:

    pkg-config --libs gtk4
  2. Build the bindings (if needed):

    python descriptgen.py
    python bindgen.py
    # OR
    ./gen.sh

πŸš€ Quick Start

Here's a simple "Hello World" application:

from gtk import * # make sure you have gtk.mojo

@register_passable("trivial")
struct App:
    @staticmethod
    fn activate(app: GTKInterface, gptr: GTKInterface):
        var win = gtk_application_window_new(app)
        gtk_window_set_title(win, "Hello Mojo-GTK!")
        gtk_window_set_default_size(win, 400, 300)
        
        var label = gtk_label_new("Hello from Mojo! πŸ”₯")
        gtk_window_set_child(win, label)
        gtk_widget_show(win)

fn main() raises:
    var app = gtk_application_new("com.example.hello", 0)
    _ = g_signal_connect_data(
        app, 
        "activate", 
        rebind[GTKInterface](App.activate),
        GTKInterface(),
        None,
        0
    )
    _ = g_application_run(app, 0, GTKInterface())

Build and run:

mojo build hello.mojo
./hello

πŸ› οΈ Scripts

descriptgen.py

Generates a JSON file containing all GTK functions, structs, and enums by parsing GTK's introspection data.

Usage:

python descriptgen.py

Output: Creates fn.json with complete API definitions.

bindgen.py

Uses the JSON file generated by descriptgen.py to create gtk.mojo, which contains all the appropriate struct and function declarations.

Usage:

python bindgen.py

Output: Generates gtk.mojo with Mojo-compatible bindings.

test-demos.py

Builds and runs all demo applications to verify the bindings work correctly,.

Usage:

python test-demos.py

Output: A table summarising all the demos that compiled and executed successfully

Build Summary
--------------------------------------------------------------------------------
File                             Errors   Warnings     Status    Retcode
--------------------------------------------------------------------------------
main.mojo                             0          1         OK        -15
widgetzoo2.mojo                       0          4         OK        -15
widgetzoo.mojo                        0          1         OK        -15
old_gtk3_sample.mojo                  0          5         OK        -15
drawingboard.mojo                     0         18         OK        -15
animations.mojo                       0          1         OK        -15
new_dialogs.mojo                      0          1         OK        -15
listview.mojo                         0          6         OK        -15
image.mojo                            0         12         OK        -15
dialogs.mojo                          0          1         OK        -15
editor.mojo                           0          1         OK        -15
opengl.mojo                           0         17         OK        -15
layout.mojo                           0          6         OK        -15
forms.mojo                            0          1         OK        -15
--------------------------------------------------------------------------------
TOTAL                                 0         75

🎨 Demo Applications

The /gtk-demos directory contains 13+ example applications demonstrating various GTK features:

Demo Description
animations.mojo Smooth CSS-based animations and transitions
dialogs.mojo Legacy dialog system for gtk4
new_dialogs.mojo Modern gtk dialog implementations
image.mojo Image loading and display
listview.mojo List and grid views with custom models
forms.mojo Form inputs and validation
layout.mojo Different layout managers (Box, Grid, etc.)
opengl.mojo OpenGL rendering in GTK
widgetzoo.mojo Showcase of all available widgets
widgetzoo2.mojo Additional widget demonstrations
old_gtk3_sample.mojo Legacy GTK3 compatibility example
drawingboard.mojo Custom drawing with Cairo
editor.mojo Simple text editor implementation
main.mojo Comprehensive application example

Run any demo:

mojo build gtk-demos/animations.mojo
./animations

πŸ“š Usage Guide

Naming Conventions

All functions and types bear a similar or exactly the same name as their C GTK counterparts, with one important exception:

  • Functions: Same as C (e.g., gtk_window_new, gtk_button_set_label)
  • Enums: Slightly differ from their C versions, (e.g GTK_ORIENTATION -> GTKOrientation)
  • Structs: Prefixed with "GTK" (e.g., GdkRGBA β†’ GTKRGBA)

Examples:

# C GTK
GdkRGBA color;
gtk_color_chooser_get_rgba(chooser, &color);

# Mojo-GTK
var rgba_ptr = LegacyUnsafePointer[GTKRGBA].alloc(1) 
gtk_color_chooser_get_rgba(dialog, rgba_ptr)             

Working with GTKInterface

Most opaque GTK pointers are represented using the GTKInterface type:

var window: GTKInterface = gtk_application_window_new(app)
var button: GTKInterface = gtk_button_new_with_label("Click Me")
gtk_window_set_child(window, button)

Function Pointers and Callbacks

Due to Mojo's current limitations with C-compatible function pointers, callbacks require a specific pattern:

  1. Create a @register_passable("trivial") struct
  2. Define a static method for your callback
  3. Pass the static method using rebind[GTKInterface]()

Example:

@register_passable("trivial")
struct ButtonHandler:
    @staticmethod
    fn on_clicked(button: GTKInterface, user_data: GTKInterface):
        print("Button clicked!")

# Connect the callback
_ = g_signal_connect_data(
    button,
    "clicked",
    rebind[GTKInterface](ButtonHandler.on_clicked),
    GTKInterface(),  # user_data
    None,
    0
)

Pattern for custom data:

@register_passable("trivial")
struct AppState:
    var counter: Int
    
    fn __init__(out self):
        self.counter = 0

@register_passable("trivial")
struct Handler:
    @staticmethod
    fn on_click(btn: GTKInterface, user_data: GTKInterface):
        var state_ptr = rebind[LegacyUnsafePointer[AppState]](user_data)
        state_ptr[].

About

Scripts for generating Mojo GTK Bindings

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors