At a Christmas party I attended a couple of weeks ago, I discovered a classic board game called Quarto. In fact, the host of the party who's worked for a major gaming company in the past asked me if I knew how to build it as a computer application. I discounted myself as a non-game-developer who only builds business applications, but then followed that by saying that if it is only a 2D game, it was simple to build. So, the challenge was on!!!
Here is the classic Quarto black board version that the party attendees played with during Christmas:
I built Quarto as a computer game in 4-5 days using Glimmer DSL for SWT!
Here is a video demo of Glimmer Quarto:
The key features in Glimmer DSL for SWT that helped me complete it very quickly are Custom Shape support (e.g. building cylinder and cube Custom Shapes and reusing to model Quarto piece Custom Shape) and the effortless Canvas Drag and Drop (e.g. designating one Custom Shape as a drag source and another as a drop target with on_drop listener). I also used affine Transforms to tilt the board by 45 degrees from a standard grid.
The top-level Quarto code is included below, followed by the code of the Quarto piece, cylinder, and cube Custom Shapes, followed by a link to the rest of the code, and then finally a quick tutorial for the Glimmer DSL for SWT Canvas Shape DSL.
Quarto
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto # Top-level Quarto board GUI code that visually maps to real GUI shell(:shell_trim, (:double_buffered unless OS.mac?)) { text 'Glimmer Quarto' minimum_size BOARD_DIAMETER + AREA_MARGIN + PIECES_AREA_WIDTH + SHELL_MARGIN*2 + (OS.linux? ? 52 : (OS.windows? ? 16 : 0)), BOARD_DIAMETER + 24 + SHELL_MARGIN*2 + (OS.linux? ? 96 : (OS.windows? ? 32 : 0)) maximum_size BOARD_DIAMETER + AREA_MARGIN + PIECES_AREA_WIDTH + SHELL_MARGIN*2 + (OS.linux? ? 52 : (OS.windows? ? 16 : 0)), BOARD_DIAMETER + 24 + SHELL_MARGIN*2 + (OS.linux? ? 96 : (OS.windows? ? 32 : 0)) background COLOR_WOOD quarto_menu_bar @board = board(game: @game, location_x: SHELL_MARGIN, location_y: SHELL_MARGIN) @available_pieces_area = available_pieces_area(game: @game, location_x: SHELL_MARGIN + BOARD_DIAMETER + AREA_MARGIN, location_y: SHELL_MARGIN) @selected_piece_area = selected_piece_area(game: @game, location_x: SHELL_MARGIN + BOARD_DIAMETER + AREA_MARGIN, location_y: SHELL_MARGIN + AVAILABLE_PIECES_AREA_HEIGHT + AREA_MARGIN) }
Piece
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto require_relative 'cylinder' require_relative 'cube' class Quarto module View class Piece include Glimmer::UI::CustomShape SIZE_SHORT = 28 SIZE_TALL = 48 BASIC_SHAPE_WIDTH = 48 BASIC_SHAPE_HEIGHT = 28 LINE_THICKNESS = 2 options :game, :model, :location_x, :location_y before_body do @background_color = model.light? ? COLOR_LIGHT_WOOD : COLOR_DARK_WOOD @size = model.short? ? SIZE_SHORT : SIZE_TALL @shape_location_x = 0 @shape_location_y = model.short? ? 20 : 0 end body { shape(location_x, location_y) { if model.is_a?(Model::Piece::Cylinder) cylinder(location_x: @shape_location_x, location_y: @shape_location_y, cylinder_height: @size, oval_width: BASIC_SHAPE_WIDTH, oval_height: BASIC_SHAPE_HEIGHT, pitted: model.pitted?, background_color: @background_color, line_thickness: LINE_THICKNESS) else cube(location_x: @shape_location_x, location_y: @shape_location_y, cube_height: @size, rectangle_width: BASIC_SHAPE_WIDTH, rectangle_height: BASIC_SHAPE_HEIGHT, pitted: model.pitted?, background_color: @background_color, line_thickness: LINE_THICKNESS) end } } end end end
Cylinder
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto class Quarto module View class Cylinder include Glimmer::UI::CustomShape DEFAULT_SIZE = 28 options :location_x, :location_y, :oval_width, :oval_height, :cylinder_height, :pitted, :background_color, :line_thickness alias pitted? pitted before_body do self.location_x ||= 0 self.location_y ||= 0 self.oval_width ||= oval_height || cylinder_height || DEFAULT_SIZE self.oval_height ||= oval_width || cylinder_height || DEFAULT_SIZE self.cylinder_height ||= oval_width || oval_height || DEFAULT_SIZE self.line_thickness ||= 1 end body { shape(location_x, location_y) { oval(0, cylinder_height, oval_width, oval_height) { background background_color oval { # draws with foreground :black and has max size within parent by default line_width line_thickness } } rectangle(0, oval_height / 2.0, oval_width, cylinder_height) { background background_color } polyline(0, oval_height / 2.0 + cylinder_height, 0, oval_height / 2.0, oval_width, oval_height / 2.0, oval_width, oval_height / 2.0 + cylinder_height) { line_width line_thickness } oval(0, 0, oval_width, oval_height) { background background_color oval { # draws with foreground :black and has max size within parent by default line_width line_thickness } } if pitted? oval(oval_width / 4.0, oval_height / 4.0, oval_width / 2.0, oval_height / 2.0) { background :black } end } } end end end
Cube
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#quarto class Quarto module View class Cube include Glimmer::UI::CustomShape DEFAULT_SIZE = 28 options :location_x, :location_y, :rectangle_width, :rectangle_height, :cube_height, :pitted, :background_color, :line_thickness alias pitted? pitted before_body do self.location_x ||= 0 self.location_y ||= 0 self.rectangle_width ||= rectangle_height || cube_height || DEFAULT_SIZE self.rectangle_height ||= rectangle_width || cube_height || DEFAULT_SIZE self.cube_height ||= rectangle_width || rectangle_height || DEFAULT_SIZE self.line_thickness ||= 1 end body { shape(location_x, location_y) { polygon(0, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height, rectangle_width, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height + rectangle_height) { background background_color } polygon(0, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height, rectangle_width, cube_height + rectangle_height / 2.0, rectangle_width / 2.0, cube_height + rectangle_height) { line_width line_thickness } rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) { background background_color } polyline(0, rectangle_height / 2.0 + cube_height, 0, rectangle_height / 2.0, rectangle_width, rectangle_height / 2.0, rectangle_width, rectangle_height / 2.0 + cube_height) { line_width line_thickness } polygon(0, rectangle_height / 2.0, rectangle_width / 2.0, 0, rectangle_width, rectangle_height / 2.0, rectangle_width / 2.0, rectangle_height) { background background_color } polygon(0, rectangle_height / 2.0, rectangle_width / 2.0, 0, rectangle_width, rectangle_height / 2.0, rectangle_width / 2.0, rectangle_height) { line_width line_thickness } line(rectangle_width / 2.0, cube_height + rectangle_height, rectangle_width / 2.0, rectangle_height) { line_width line_thickness } if pitted? oval(rectangle_width / 4.0, rectangle_height / 4.0, rectangle_width / 2.0, rectangle_height / 2.0) { background :black } end } } end end end
Rest of the Quarto code:
Views:
https://github.com/AndyObtiva/glimmer-dsl-swt/tree/master/samples/elaborate/quarto/view
Models:
https://github.com/AndyObtiva/glimmer-dsl-swt/tree/master/samples/elaborate/quarto/model
Now, let us get into the Canvas Shape DSL tutorial.
Canvas Shape DSL Tutorial
Below are examples of using the Canvas Shape DSL in Glimmer DSL for SWT.
Example of line
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white line(30, 30, 170, 170) { foreground :red line_width 3 } } }.open
Example of rectangle
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white rectangle(30, 50, 140, 100) { background :yellow } rectangle(30, 50, 140, 100) { foreground :red line_width 3 } } }.open
Example of rectangle
with round corners having 60 degree angles by default (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white rectangle(30, 50, 140, 100, round: true) { background :yellow } rectangle(30, 50, 140, 100, round: true) { foreground :red line_width 3 } } }.open
Example of rectangle
with round corners having different horizontal and vertical angles (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white rectangle(30, 50, 140, 100, 40, 80) { background :yellow } rectangle(30, 50, 140, 100, 40, 80) { foreground :red line_width 3 } } }.open
Example of oval
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white oval(30, 50, 140, 100) { background :yellow } oval(30, 50, 140, 100) { foreground :red line_width 3 } } }.open
Example of arc
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white arc(30, 30, 140, 140, 0, 270) { background :yellow } arc(30, 30, 140, 140, 0, 270) { foreground :red line_width 3 } } }.open
Example of polyline
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) { foreground :red line_width 3 } } }.open
Example of polygon
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) { background :yellow } polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) { foreground :red line_width 3 } } }.open
Example of text
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white text(" This is \n rendered text ", 30, 50) { background :yellow foreground :red font height: 25, style: :italic rectangle { # automatically scales to match text extent foreground :red line_width 3 } } } }.open
Example of image
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 512, 542 canvas { background :white image(File.expand_path('icons/scaffold_app.png', __dir__), 0, 5) } }.open
Example of image
pre-built with a smaller height (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer @image_object = image(File.expand_path('icons/scaffold_app.png', __dir__), height: 200) shell { text 'Canvas Shape DSL' minimum_size 200, 230 canvas { background :white image(@image_object, 0, 5) } }.open
Example of setting background_pattern
attribute to a horizontal gradient (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white oval(30, 30, 140, 140) { background_pattern 0, 0, 200, 0, rgb(255, 255, 0), rgb(255, 0, 0) } } }.open
Example of setting foreground_pattern
attribute to a vertical gradient (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white oval(30, 30, 140, 140) { foreground_pattern 0, 0, 0, 200, :blue, :green line_width 10 } } }.open
Example of setting line_style
attribute to :dashdot
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white oval(30, 50, 140, 100) { background :yellow } oval(30, 50, 140, 100) { foreground :red line_width 3 line_style :dashdot } } }.open
Example of setting line_width
attribute to 10
, line_join
attribute to :miter
(default) and line_cap
attribute to :flat
(default) (you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) { foreground :red line_width 10 line_join :miter line_cap :flat } } }.open
Example of setting line_width
attribute to 10
, line_join
attribute to :round
and line_cap
attribute to :round
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) { foreground :red line_width 10 line_join :round line_cap :round } } }.open
Example of setting line_width
attribute to 10
, line_join
attribute to :bevel
and line_cap
attribute to :square
(you may copy/paste in girb
):
require 'glimmer-dsl-swt' include Glimmer shell { text 'Canvas Shape DSL' minimum_size 200, 220 canvas { background :white polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) { foreground :red line_width 10 line_join :bevel line_cap :square } } }.open
Happy Glimmering!
Top comments (0)