Glimmer DSL for LibUI 0.8.0 (Fukuoka Award Winning Ruby Desktop Development Cross-Platform Native GUI Library) has been released with a new feature called Composite Shape (already in Glimmer DSL for SWT), which enables building new visual concepts by aggregating multiple smaller shapes within a parent composite shape using relative positioning and inherited `fill`/`stroke` colors.
That is done by using the new `shape` keyword (alias for `composite_shape`) and nesting other Glimmer DSL for LibUI area shapes within it, like `polygon`, `rectangle`, or `circle`. Also, you can declare mouse listeners on a composite `shape`, and it will automatically figure out if a mouse click point is within any of its aggregated shapes using the correct geometric algorithms, courtesy of the perfect-shape gem.
For example, you could build a `cube` by aggregating `polygon`s, a `polyline`, a `rectangle`, and a `line` within a `shape`. Afterwards, you could use the new `cube` visual component directly.
A new example "examples/basic_composite_shape.rb" (shown below) has been included to demonstrate the new Composite Shape feature.
- Support `composite_shape` keyword (alias: `shape`) as aggregate (composite) shape that can have arbitrary shapes, text, transforms underneath, which inherit its `fill`/`stroke` colors and `transform`. `composite_shape` also supports nesting mouse listeners, which check mouse click point containment against all nested shapes automatically.
- New `examples/basic_composite_shape.rb` with use of `shape` + drag and drop support for moving shapes and click support for changing shape colors
- Invert `Glimmer::LibUI::ControlProxy::KEYWORD_ALIASES` to enable adding multiple aliases per keyword
- Support `Glimmer::LibUI::Shape::KEYWORD_ALIASES` to enable adding multiple aliases per keyword
- Small update for `examples/button_counter.rb`
require 'glimmer-dsl-libui' | |
class BasicCompositeShape | |
include Glimmer::LibUI::Application | |
body { | |
window { | |
title 'Basic Composite Shape' | |
content_size 200, 225 | |
@area = area { | |
rectangle(0, 0, 200, 225) { | |
fill :white | |
} | |
7.times do |n| | |
x_location = (rand*125).to_i%200 + (rand*15).to_i | |
y_location = (rand*125).to_i%200 + (rand*15).to_i | |
shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130] | |
shape_size = 20+n | |
cube( | |
location_x: x_location, | |
location_y: y_location, | |
rectangle_width: shape_size*2, | |
rectangle_height: shape_size, | |
cube_height: shape_size*2, | |
background_color: shape_color, | |
line_thickness: 2 | |
) { |the_shape| | |
on_mouse_up do |area_mouse_event| | |
# Change color on mouse up without dragging | |
if @drag_shape.nil? | |
background_color = [rand(255), rand(255), rand(255)] | |
the_shape.fill = background_color | |
end | |
end | |
on_mouse_drag_start do |area_mouse_event| | |
@drag_shape = the_shape | |
@drag_x = area_mouse_event[:x] | |
@drag_y = area_mouse_event[:y] | |
end | |
on_mouse_drag do |area_mouse_event| | |
if @drag_shape && @drag_x && @drag_y | |
drag_distance_width = area_mouse_event[:x] - @drag_x | |
drag_distance_height = area_mouse_event[:y] - @drag_y | |
@drag_shape.x += drag_distance_width | |
@drag_shape.y += drag_distance_height | |
@drag_x = area_mouse_event[:x] | |
@drag_y = area_mouse_event[:y] | |
end | |
end | |
on_mouse_drop do |area_mouse_event| | |
@drag_shape = nil | |
@drag_x = nil | |
@drag_y = nil | |
end | |
} | |
end | |
# this general area on_mouse_drag listener is needed to ensure that dragging a shape | |
# outside of its boundaries would still move the dragged shape | |
on_mouse_drag do |area_mouse_event| | |
if @drag_shape && @drag_x && @drag_y | |
drag_distance_width = area_mouse_event[:x] - @drag_x | |
drag_distance_height = area_mouse_event[:y] - @drag_y | |
@drag_shape.x += drag_distance_width | |
@drag_shape.y += drag_distance_height | |
@drag_x = area_mouse_event[:x] | |
@drag_y = area_mouse_event[:y] | |
end | |
end | |
on_mouse_drop do |area_mouse_event| | |
@drag_shape = nil | |
@drag_x = nil | |
@drag_y = nil | |
end | |
} | |
} | |
} | |
# method-based custom shape using `shape` keyword as a composite shape containing nested shapes | |
# that are declared with relative positioning | |
def cube(location_x: 0, | |
location_y: 0, | |
rectangle_width: nil, | |
rectangle_height: nil, | |
cube_height: nil, | |
background_color: :brown, | |
line_thickness: 1, | |
&content_block) | |
default_size = 28 | |
rectangle_width ||= rectangle_height || cube_height || default_size | |
rectangle_height ||= rectangle_width || cube_height || default_size | |
cube_height ||= rectangle_width || rectangle_height || default_size | |
foreground_color = [0, 0, 0, thickness: line_thickness] | |
# the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape | |
# like a cube containing polygons, a polyline, a rectangle, and a line | |
# with the fill and stroke colors getting inherited by all children that do not specify them | |
shape(location_x, location_y) { |the_shape| | |
fill background_color | |
stroke foreground_color | |
bottom = 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) { | |
# inherits fill property from parent shape if not set | |
# inherits stroke property from parent shape if not set | |
} | |
body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) { | |
# inherits fill property from parent shape if not set | |
# stroke is overridden to ensure a different value from parent | |
stroke thickness: 0 | |
} | |
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) { | |
# inherits stroke property from parent shape if not set | |
} | |
top = polygon(0, rectangle_height / 2.0, | |
rectangle_width / 2.0, 0, | |
rectangle_width, rectangle_height / 2.0, | |
rectangle_width / 2.0, rectangle_height) { | |
# inherits fill property from parent shape if not set | |
# inherits stroke property from parent shape if not set | |
} | |
line(rectangle_width / 2.0, cube_height + rectangle_height, | |
rectangle_width / 2.0, rectangle_height) { | |
# inherits stroke property from parent shape if not set | |
} | |
content_block&.call(the_shape) | |
} | |
end | |
end | |
BasicCompositeShape.launch | |
No comments:
Post a Comment