Glimmer DSL for LibUI Beta Release
Tags: ruby, desktop, gui, dsl
Published: 2021-10-08 15:50:30


I recently [mentioned](https://dev.to/andyobtiva/glimmer-dsl-for-swt-4-21-new-swt-jruby-jdk-2j2g) that a new [Glimmer](https://github.com/AndyObtiva/glimmer) DSL was brewing; that is [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui). 

It has been a passion project for the last 3 weeks that ended today with [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) becoming feature-complete and moving from Alpha to Beta.

What is LibUI?

[LibUI](https://github.com/kojix2/LibUI) is a thin Ruby wrapper around [libui](https://github.com/andlabs/libui), a relatively new C GUI library that renders native controls on every platform (similar to [SWT](https://www.eclipse.org/swt/), but without the heavy weight of the [Java Virtual Machine](https://www.java.com/en/)).

One of the biggest benefits of [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) is that it is a prerequisite-free MRI CRuby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!

The main trade-off in using [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha libui as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the Ruby gem. Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. LibUI is a promising new GUI toolkit that might prove quite worthy in the future.

One nifty innovation that this project came up with is implicit data-binding for table data as you will see in the table example below.

[Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
- Declarative DSL syntax that visually maps to the GUI control hierarchy
- Convention over configuration via smart defaults and automation of low-level details
- Requiring the least amount of syntax possible to build GUI
- Custom Control support

## Hello, World!

![hello world](https://github.com/AndyObtiva/glimmer-dsl-libui/raw/master/images/glimmer-dsl-libui-mac-basic-window.png)

```ruby
require 'glimmer-dsl-libui'

include Glimmer

window('hello world').show
```

## Basic Table Progress Bar

![basic table progress bar](https://github.com/AndyObtiva/glimmer-dsl-libui/raw/master/images/glimmer-dsl-libui-linux-basic-table-progress-bar.png)

```ruby
require 'glimmer-dsl-libui'

include Glimmer

data = [
  ['task 1', 0],
  ['task 2', 15],
  ['task 3', 100],
  ['task 4', 75],
  ['task 5', -1],
]

window('Task Progress', 300, 200) {
  vertical_box {
    table {
      text_column('Task')
      progress_bar_column('Progress')

      cell_rows data # implicit data-binding
    }
    
    button('Mark All As Done') {
      stretchy false
      
      on_clicked do
        data.each_with_index do |row_data, row|
          data[row][1] = 100 # automatically updates table due to implicit data-binding
        end
      end
    }
  }
}.show
```

## Area Gallery

![area gallery](https://github.com/AndyObtiva/glimmer-dsl-libui/raw/master/images/glimmer-dsl-libui-mac-area-gallery.png)

```ruby
require 'glimmer-dsl-libui'

include Glimmer

window('Area Gallery', 400, 400) {
  area {
    path { # declarative stable path
      square(0, 0, 100)
      square(100, 100, 400)

      fill r: 102, g: 102, b: 204
    }
    path { # declarative stable path
      rectangle(0, 100, 100, 400)
      rectangle(100, 0, 400, 100)

      # linear gradient (has x0, y0, x1, y1, and stops)
      fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
    }
    path { # declarative stable path
      figure(100, 100) {
        line(100, 400)
        line(400, 100)
        line(400, 400)

        closed true
      }

      fill r: 202, g: 102, b: 104, a: 0.5
      stroke r: 0, g: 0, b: 0
    }
    path { # declarative stable path
      figure(0, 0) {
        bezier(200, 100, 100, 200, 400, 100)
        bezier(300, 100, 100, 300, 100, 400)
        bezier(100, 300, 300, 100, 400, 400)

        closed true
      }

      fill r: 202, g: 102, b: 204, a: 0.5
      stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
    }
    path { # declarative stable path
      arc(400, 220, 180, 90, 90, false)

      # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
      fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
      stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
    }
    path { # declarative stable path
      circle(200, 200, 90)

      fill r: 202, g: 102, b: 204, a: 0.5
      stroke r: 0, g: 0, b: 0, thickness: 2
    }
    text(160, 40, 100) { # x, y, width
      string {
        font family: 'Times', size: 14
        color :black
        
        'Area Gallery'
      }
    }
    
    on_mouse_event do |area_mouse_event|
      p area_mouse_event
    end
    
    on_mouse_moved do |area_mouse_event|
      puts 'moved'
    end
    
    on_mouse_down do |area_mouse_event|
      puts 'mouse down'
    end
    
    on_mouse_up do |area_mouse_event|
      puts 'mouse up'
    end
    
    on_mouse_drag_started do |area_mouse_event|
      puts 'drag started'
    end
    
    on_mouse_dragged do |area_mouse_event|
      puts 'dragged'
    end
    
    on_mouse_dropped do |area_mouse_event|
      puts 'dropped'
    end
    
    on_mouse_entered do
      puts 'entered'
    end
    
    on_mouse_exited do
      puts 'exited'
    end
    
    on_key_event do |area_key_event|
      p area_key_event
    end
    
    on_key_up do |area_key_event|
      puts 'key up'
    end
    
    on_key_down do |area_key_event|
      puts 'key down'
    end
  }
}.show
```

Check out many more examples at [the project page](https://github.com/AndyObtiva/glimmer-dsl-libui).

Keep in mind that just like with [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui), you can build a complete and elaborate DSL in [Glimmer](https://github.com/AndyObtiva/glimmer) in as fast as 3 weeks (with the first version done in a matter of hours). [Glimmer](https://github.com/AndyObtiva/glimmer) offers ultimate flexibility in [sculpting a DSL](https://github.com/AndyObtiva/glimmer#dsl-engine) exactly the way you want whether it involves arguments, keyword arguments, block variables, nested attributes, or content blocks. Use [Glimmer](https://github.com/AndyObtiva/glimmer) for your next DSL project.

Happy [Glimmering](https://github.com/AndyObtiva/glimmer)!