Andy Maleh
Posted on March 3, 2021
Glimmer DSL for SWT had a relatively major release in v4.18.6.0 and its follow-up v4.18.6.1 to introduce a new Sub-DSL called the Canvas Path DSL, which enables creating paths consisting of the following:
- Points
- Lines
- Quadratic Bezier Curves
- Cubic Bezier Curves
Unlike other imperative graphing GUI libraries, Glimmer facilitates declarative creation of paths. In other words, it requires no explicit use of move_to, curve_to, etc... (even if possible directly via SWT), yet clear and concise declaration of paths via nouns instead, such as path
, point
, line
, quad
, and cubic
, thus enabling higher code understandability, maintainability, and productivity.
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/development/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-path-dsl
require 'glimmer-dsl-swt'
include Glimmer
shell {
text 'Canvas Path Example'
minimum_size 300, 300
canvas {
path {
foreground :black
250.times {|n|
cubic(n + n%30, n+ n%50, 40, 40, 70, 70, n + 20 + n%30, n%30*-1 * n%50)
}
}
}
}.open
In fact, two new samples have been added to Glimmer DSL for SWT to demo the new Canvas Path DSL capabilities.
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/development/docs/reference/GLIMMER_SAMPLES.md#hello-canvas-path
require 'glimmer-dsl-swt'
include Glimmer
shell {
text 'Hello, Canvas Path!'
minimum_size 800, 700
background :white
canvas {
background :white
text('line', 15, 200) {
foreground :red
}
@path1 = path {
antialias :on
foreground :red
}
text('quad', 15, 300) {
foreground :dark_green
}
@path2 = path {
antialias :on
foreground :dark_green
}
text('cubic', 15, 400) {
foreground :blue
}
@path3 = path {
antialias :on
foreground :blue
}
}
on_swt_show {
Thread.new {
y1 = y2 = y3 = 300
800.times.each do |x|
x += 55
x1 = x - 2
x2 = x - 1
x3 = x
y1 = y3
y2 = y1
y3 = [[y3 + (rand*24 - 12), 0].max, 700].min
@path1.content {
line(x1, y1 - 100)
}
if x % 2 == 0
@path2.content {
quad(x1, y1, x2, y2)
}
end
if x % 3 == 0
@path3.content {
cubic(x1, y1 + 100, x2, y2 + 100, x3, y3 + 100)
}
end
sleep(0.01)
end
}
}
}.open
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/development/docs/reference/GLIMMER_SAMPLES.md#stock-ticker
require 'glimmer-dsl-swt'
# This Sample is an Early Alpha (New Canvas Path DSL Feature)
class StockTicker
class Stock
class << self
attr_writer :stock_price_min, :stock_price_max
def stock_price_min
@stock_price_min ||= 1
end
def stock_price_max
@stock_price_max ||= 600
end
end
attr_reader :name, :stock_prices
attr_accessor :stock_price
def initialize(name, stock_price)
@name = name
@stock_price = stock_price
@stock_prices = [@stock_price]
@delta_sign = 1
start_new_trend!
end
def tick!
@tick_count = @tick_count.to_i + 1
delta = @tick_count%@trend_length
if delta == 0
@delta_sign *= -1
start_new_trend!
end
stock_prices << self.stock_price = [[@stock_price + @delta_sign*delta, Stock.stock_price_min].max, Stock.stock_price_max].min
end
def start_new_trend!
@trend_length = (rand*12).to_i + 1
end
end
include Glimmer::UI::CustomShell
before_body {
@stocks = [
Stock.new('DELL', 81),
Stock.new('AAPL', 121),
Stock.new('MSFT', 232),
Stock.new('ADBE', 459),
]
@stock_colors = [:red, :dark_green, :blue, :dark_magenta]
max_stock_name_width = 0
left_margin = 5
@tabs = ['Lines', 'Quadratic Bezier Curves', 'Cubic Bezier Curves', 'Points'].map {|title| {title: title, stock_paths: [], stock_transforms: []}}
@stocks.each_with_index do |stock, stock_index|
observe(stock, :stock_price) do |new_price|
begin
@tabs.each do |tab|
new_x = stock.stock_prices.count - 1
new_y = @tabs.first[:canvas].bounds.height - new_price - 1
max_stock_name_width = tab[:text]&.bounds&.width if tab[:text]&.bounds&.width.to_f > max_stock_name_width
if new_x > 0
case tab[:title]
when 'Cubic Bezier Curves'
if new_x%3 == 0 && stock.stock_prices[new_x] && stock.stock_prices[new_x - 1] && stock.stock_prices[new_x - 2]
tab[:stock_paths][stock_index].content {
cubic(new_x - 2, @tabs.first[:canvas].bounds.height - stock.stock_prices[new_x - 2] - 1, new_x - 1, @tabs.first[:canvas].bounds.height - stock.stock_prices[new_x - 1] - 1, new_x, new_y)
tab[:stock_transforms][stock_index] ||= transform {
translate max_stock_name_width + 5 + left_margin, tab[:text].bounds.height / 2.0
}
}
end
when 'Quadratic Bezier Curves'
if new_x%2 == 0 && stock.stock_prices[new_x] && stock.stock_prices[new_x - 1]
tab[:stock_paths][stock_index].content {
quad(new_x - 1, @tabs.first[:canvas].bounds.height - stock.stock_prices[new_x - 1] - 1, new_x, new_y)
tab[:stock_transforms][stock_index] ||= transform {
translate max_stock_name_width + 5 + left_margin, tab[:text].bounds.height / 2.0
}
}
end
when 'Lines'
tab[:stock_paths][stock_index].content {
line(new_x, new_y)
tab[:stock_transforms][stock_index] ||= transform {
translate max_stock_name_width + 5 + left_margin, tab[:text].bounds.height / 2.0
}
}
when 'Points'
tab[:stock_paths][stock_index].content {
point(new_x, new_y)
tab[:stock_transforms][stock_index] ||= transform {
translate max_stock_name_width + 5 + left_margin, tab[:text].bounds.height / 2.0
}
}
end
new_x_location = new_x + max_stock_name_width + 5 + left_margin + 5
canvas_width = tab[:canvas].bounds.width
if new_x_location > canvas_width
tab[:canvas].set_size(new_x_location, @tabs.first[:canvas].bounds.height)
tab[:canvas].cursor = :hand
tab[:scrolled_composite].set_min_size(new_x_location, @tabs.first[:canvas].bounds.height)
tab[:scrolled_composite].set_origin(tab[:scrolled_composite].origin.x + 1, tab[:scrolled_composite].origin.y) if (tab[:scrolled_composite].origin.x + tab[:scrolled_composite].client_area.width) == canvas_width
end
else
tab[:canvas].content {
tab[:text] = text(stock.name, new_x + left_margin, new_y) {
foreground @stock_colors[stock_index]
}
}
end
end
rescue => e
Glimmer::Config.logger.error {e.full_message}
end
end
end
}
after_body {
@thread = Thread.new {
loop {
@stocks.each(&:tick!)
sleep(0.01)
}
}
}
body {
shell {
fill_layout {
margin_width 15
margin_height 15
}
text 'Stock Ticker'
minimum_size 650, 650
background :white
@tab_folder = tab_folder {
@tabs.each do |tab|
tab_item {
fill_layout {
margin_width 0
margin_height 0
}
text tab[:title]
tab[:scrolled_composite] = scrolled_composite {
tab[:canvas] = canvas {
background :white
@stocks.count.times do |stock_index|
tab[:stock_paths][stock_index] = path {
antialias :on
foreground @stock_colors[stock_index]
}
end
on_mouse_down {
@drag_detected = false
}
on_drag_detected { |drag_detect_event|
@drag_detected = true
@drag_start_x = drag_detect_event.x
@drag_start_y = drag_detect_event.y
}
on_mouse_move { |mouse_event|
if @drag_detected
origin = tab[:scrolled_composite].origin
new_x = origin.x - (mouse_event.x - @drag_start_x)
new_y = origin.y - (mouse_event.y - @drag_start_y)
tab[:scrolled_composite].set_origin(new_x, new_y)
end
}
on_mouse_up { |mouse_event|
@drag_detected = false
}
}
}
}
end
}
on_swt_show {
Stock.stock_price_min = 25
Stock.stock_price_max = @tabs.first[:canvas].bounds.height - 6
# pre-initialize all tabs by selecting them so that they render content when they are later in the background
@tab_folder.items.each do |item|
@tab_folder.selection = item
end
@tab_folder.selection = @tab_folder.items.first
}
on_widget_disposed {
@thread.kill # safe to kill as data is in memory only
}
}
}
end
StockTicker.launch
There is nothing more productive than Glimmer DSL for SWT when it comes to building cross-platform desktop applications, bar none! And, it is only getting better every day. Start learning Glimmer DSL for SWT today by reading Glimmer blog posts and building your own apps while reporting feedback for improvement. It is about time Ruby's become the world leading language for desktop development, not just web.
Happy Glimmering!
Posted on March 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.