DEV Community

Cover image for Glimmer DSL for LibUI Beta Release
Andy Maleh
Andy Maleh

Posted on

Glimmer DSL for LibUI Beta Release

I recently mentioned that a new Glimmer DSL was brewing; that is Glimmer DSL for LibUI.

It has been a passion project for the last 3 weeks that ended today with Glimmer DSL for LibUI becoming feature-complete and moving from Alpha to Beta.

What is LibUI?

LibUI is a thin Ruby wrapper around libui, a relatively new C GUI library that renders native controls on every platform (similar to SWT, but without the heavy weight of the Java Virtual Machine).

One of the biggest benefits of Glimmer DSL for LibUI as opposed to Glimmer DSL for 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 as opposed to Glimmer DSL for SWT or Glimmer DSL for Tk is the fact that SWT and 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 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, 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 aims to provide a DSL similar to the Glimmer DSL for 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

require 'glimmer-dsl-libui'

include Glimmer

window('hello world').show
Enter fullscreen mode Exit fullscreen mode

Basic Table Progress Bar

basic table progress bar

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
Enter fullscreen mode Exit fullscreen mode

Area Gallery

area gallery

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
Enter fullscreen mode Exit fullscreen mode

Check out many more examples at the project page.

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

Happy Glimmering!

Discussion (0)