Thursday, March 04, 2021

Glimmer DSL for SWT Custom Shapes

Glimmer DSL for SWT just had a new feature release (4.18.7.0) introducing the concept of custom shapes, which is similar to custom widgets, except it is applied to shapes from the Canvas Shape DSL.

In fact, it supports both a method-based approach and a class-based more modular approach for defining new custom shape keywords.

This enables higher order visual concepts, such as defining a car from polygons, defining a beach_scene from tree, sand, and sunset, etc... The possibilities are endless!

This realizes Lisp's ultimate vision of code as data and data as code by being able to abstract and hide an entire concept like `boat` that is consisting of hundreds of `polygon` shapes behind a single keyword representing `boat` data, which in turn embodies the entire `boat` shape visual code. Just imagine the possibilities of having something like this with Ruby's ultra-fluid pliable syntax! It is time Ruby upended all other languages in desktop development, no!?! You can now productively enlarge your GUI DSL vocabulary endlessly and compose ever higher visual concepts as well as decorations for existing widgets. 

I will let the newly added code samples explain more about custom shapes.

Hello, Shape!

This sample enables leveraging the generic `shape` keyword as a sort of a shape composite (similar to widget `composite`) that contains other nested shapes and defines common shared attributes for all of them. It uses that keyword in fact to build a method-based custom shape.


# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#hello-shape
require 'glimmer-dsl-swt'
class HelloShape
include Glimmer::UI::CustomShell
body {
shell {
text 'Hello, Shape!'
minimum_size 200, 225
@canvas = canvas {
background :white
15.times { |n|
x_location = (rand*125).to_i%200 + (rand*25).to_i
y_location = (rand*125).to_i%200 + (rand*25).to_i
foreground_color = rgb(rand*255, rand*255, rand*255)
stick_figure(x_location, y_location, 50, 50) {
foreground foreground_color
}
}
}
}
}
# method-based custom shape using `shape` keyword as a composite shape containing nested shapes
def stick_figure(x, y, width, height, &block)
head_width = width*0.2
head_height = height*0.2
trunk_height = height*0.4
extremity_length = height*0.4
shape(x + head_width/2.0 + extremity_length, y) {
# common attributes go here before nested shapes
block.call # invoking content block (e.g. used from the outside to set foreground)
# nested shapes go here
oval(0, 0, head_width, head_height)
line(head_width/2.0, head_height, head_width/2.0, head_height + trunk_height)
line(head_width/2.0, head_height + trunk_height, head_width/2.0 + extremity_length, head_height + trunk_height + extremity_length)
line(head_width/2.0, head_height + trunk_height, head_width/2.0 - extremity_length, head_height + trunk_height + extremity_length)
line(head_width/2.0, head_height*2, head_width/2.0 + extremity_length, head_height + trunk_height - extremity_length)
line(head_width/2.0, head_height*2, head_width/2.0 - extremity_length, head_height + trunk_height - extremity_length)
}
end
end
HelloShape.launch



Hello, Custom Shape!

This reimplements the previous sample with a class-based custom shape definition instead of method-based. This enables higher separation of concerns and extraction of code into a separate class, which is useful when defining highly complex and parametric shapes, resulting in cleaner code for better maintainability. It additionally provides support for defining default options and before_body/after_body hooks. 


# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#hello-custom-shape
require 'glimmer-dsl-swt'
# Creates a class-based custom shape representing the `stick_figure` keyword by convention
class StickFigure
include Glimmer::UI::CustomShape
options :x, :y, :width, :height
before_body {
@head_width = width*0.2
@head_height = height*0.2
@trunk_height = height*0.4
@extremity_length = height*0.4
}
body {
shape(x + @head_width/2.0 + @extremity_length, y) {
oval(0, 0, @head_width, @head_height)
line(@head_width/2.0, @head_height, @head_width/2.0, @head_height + @trunk_height)
line(@head_width/2.0, @head_height + @trunk_height, @head_width/2.0 + @extremity_length, @head_height + @trunk_height + @extremity_length)
line(@head_width/2.0, @head_height + @trunk_height, @head_width/2.0 - @extremity_length, @head_height + @trunk_height + @extremity_length)
line(@head_width/2.0, @head_height*2, @head_width/2.0 + @extremity_length, @head_height + @trunk_height - @extremity_length)
line(@head_width/2.0, @head_height*2, @head_width/2.0 - @extremity_length, @head_height + @trunk_height - @extremity_length)
}
}
end
class HelloCustomShape
include Glimmer::UI::CustomShell
WIDTH = 220
HEIGHT = 235
body {
shell {
text 'Hello, Custom Shape!'
minimum_size WIDTH, HEIGHT
@canvas = canvas {
background :white
15.times { |n|
x_location = (rand*WIDTH/2).to_i%WIDTH + (rand*15).to_i
y_location = (rand*HEIGHT/2).to_i%HEIGHT + (rand*15).to_i
foreground_color = rgb(rand*255, rand*255, rand*255)
a_stick_figure = stick_figure(x: x_location, y: y_location, width: 35+n*2, height: 35+n*2) {
foreground foreground_color
drag_and_move true
# on mouse click, change color
on_mouse_up do
a_stick_figure.foreground = rgb(rand*255, rand*255, rand*255)
end
}
}
}
}
}
end
HelloCustomShape.launch
view raw gistfile1.txt hosted with ❤ by GitHub


Happy Glimmering!

No comments: