glimmer-dsl-libui 0.7.8 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +171 -16
- data/VERSION +1 -1
- data/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md +15 -30
- data/examples/basic_composite_shape.rb +145 -0
- data/examples/button_counter.rb +1 -1
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/control_expression.rb +1 -2
- data/lib/glimmer/dsl/libui/shape_expression.rb +1 -0
- data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +1 -0
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +7 -3
- data/lib/glimmer/libui/control_proxy.rb +5 -5
- data/lib/glimmer/libui/perfect_shaped.rb +9 -1
- data/lib/glimmer/libui/shape/arc.rb +8 -4
- data/lib/glimmer/libui/shape/bezier.rb +8 -4
- data/lib/glimmer/libui/shape/circle.rb +8 -4
- data/lib/glimmer/libui/shape/composite_shape.rb +101 -0
- data/lib/glimmer/libui/shape/figure.rb +8 -4
- data/lib/glimmer/libui/shape/line.rb +8 -4
- data/lib/glimmer/libui/shape/polybezier.rb +9 -5
- data/lib/glimmer/libui/shape/polygon.rb +9 -5
- data/lib/glimmer/libui/shape/polyline.rb +9 -5
- data/lib/glimmer/libui/shape/rectangle.rb +8 -4
- data/lib/glimmer/libui/shape/square.rb +7 -3
- data/lib/glimmer/libui/shape.rb +142 -6
- data/lib/glimmer/libui.rb +15 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c03b331e5839b335cf2945538ed66eb6faf988f6ec6230f41abbb3f4fb918159
|
4
|
+
data.tar.gz: 7e2a9da5462232b63f28602811b81b15d28e20a10f322058513142e0a103ceb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8153d251be4588058275826d42a1eace167148a25a9b54b3906948438888532276773143b4f26b21ae7fab4cc071bca33f36da8db6627cef5c38bb6ce273e6e4
|
7
|
+
data.tar.gz: '0921ce14a33189f3145a72536c2feed44626a4ace129ea2978581d17514bcf048c1b70b58eb6a8fd7207b58b1bfd1d5cbb5c4bc6bc9ece7e3acc223d6e95d8d8'
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.8.0
|
4
|
+
|
5
|
+
- 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`
|
6
|
+
- New `examples/basic_composite_shape.rb` with use of `shape` + drag and drop support for moving shapes and click support for changing shape colors
|
7
|
+
- Invert `Glimmer::LibUI::ControlProxy::KEYWORD_ALIASES` to enable adding multiple aliases per keyword
|
8
|
+
- Support `Glimmer::LibUI::Shape::KEYWORD_ALIASES` to enable adding multiple aliases per keyword
|
9
|
+
- Small update for `examples/button_counter.rb`
|
10
|
+
|
3
11
|
## 0.7.8
|
4
12
|
|
5
13
|
- Upgrade to `perfect-shape` gem version 1.0.8 to fix a crashing issue in `examples/shape_coloring.rb`
|
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.8.0
|
2
2
|
## Prerequisite-Free Ruby Desktop Development GUI Library
|
3
3
|
### The Quickest Way From Zero To GUI
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
|
5
5
|
[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
6
6
|
|
7
|
+
**[If You Liked Shoes, You'll Love Glimmer!](https://github.com/AndyObtiva/glimmer#faq)**
|
8
|
+
|
7
9
|
(**[Fukuoka Ruby Award Competition 2022 Special Award Winner](https://andymaleh.blogspot.com/2022/02/glimmer-dsl-for-libui-wins-fukuoka-ruby.html)** [[Award Announcement]](http://www.digitalfukuoka.jp/topics/187?locale=ja))
|
8
10
|
|
9
11
|
(**[***RubyConf 2022 Talk - Building Native GUI Apps in Ruby***](https://andymaleh.blogspot.com/2023/02/rubyconf-2022-talk-video-for-building.html)**)
|
@@ -335,19 +337,7 @@ Mac | Windows | Linux
|
|
335
337
|
|
336
338
|
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is regularly catching up with changes in the C [libui-ng](https://github.com/libui-ng/libui-ng) library API and in beta mode. The C [libui-ng](https://github.com/libui-ng/libui-ng) is still mid-alpha, which is why [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) cannot be declared v1.0.0 yet. Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
|
337
339
|
|
338
|
-
|
339
|
-
DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
340
|
-
----|-----------|---------|------------------|------|------|--------
|
341
|
-
[Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](https://github.com/AndyObtiva/glimmer-dsl-swt) | Mac / Windows / Linux | Yes | Yes (Canvas Shape DSL) | Very Mature / Scaffolding / Native Executable Packaging / Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint | Java / JRuby
|
342
|
-
[Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)](https://github.com/AndyObtiva/glimmer-dsl-opal) | All Web Browsers | No | Yes (Canvas Shape DSL) | Simpler than All JavaScript Technologies / Auto-Webify Desktop Apps | Setup Process / Only Rails 5 Support for Now | Rails
|
343
|
-
[Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-libui) | Mac / Windows / Linux | Yes | Yes (Area API) | Fast Startup Time / Light Memory Footprint | LibUI is an Incomplete Mid-Alpha Only | None Other Than MRI Ruby
|
344
|
-
[Glimmer DSL for Tk (Ruby Tk Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-tk) | Mac / Windows / Linux | Some Native-Themed Widgets (Not Truly Native) | Yes (Canvas) | Fast Startup Time / Light Memory Footprint | Complicated Setup / Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
|
345
|
-
[Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-gtk) | Mac / Windows / Linux | Only on Linux | Yes (Cairo) | Complete Access to GNOME Features on Linux (Forte) | Not Native on Mac and Windows | None Other Than MRI Ruby on Linux / Brew Packages on Mac / MSYS & MING Toolchains on Windows / MRI Ruby
|
346
|
-
[Glimmer DSL for FX (FOX Toolkit Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-fx) | Mac (requires XQuartz) / Windows / Linux | No | Yes (Canvas) | No Prerequisites on Windows (Forte Since Binaries Are Included Out of The Box) | Widgets Do Not Look Native / Mac Usage Obtrusively Starts XQuartz | None Other Than MRI Ruby on Windows / XQuarts on Mac / MRI Ruby
|
347
|
-
[Glimmer DSL for JFX (JRuby JavaFX Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-jfx) | Mac / Windows / Linux | No | Yes (javafx.scene.shape and javafx.scene.canvas) | Rich in Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby / JavaFX SDK
|
348
|
-
[Glimmer DSL for Swing (JRuby Swing Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-swing) | Mac / Windows / Linux | No | Yes (Java2D) | Very Mature | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby
|
349
|
-
[Glimmer DSL for XML (& HTML)](https://github.com/AndyObtiva/glimmer-dsl-xml) | All Web Browsers | No | Yes (SVG) | Programmable / Lighter-weight Than Actual XML | XML Elements Are Sometimes Not Well-Named (Many Types of Input) | None
|
350
|
-
[Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) | All Web Browsers | No | Yes | Programmable | CSS Is Over-Engineered / Too Many Features To Learn | None
|
340
|
+
Learn more about the differences between various [Glimmer](https://github.com/AndyObtiva/glimmer) DSLs by looking at the **[Glimmer DSL Comparison Table](https://github.com/AndyObtiva/glimmer#glimmer-dsl-comparison-table)**.
|
351
341
|
|
352
342
|
## Table of Contents
|
353
343
|
|
@@ -373,6 +363,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
|
373
363
|
- [Area Listeners](#area-listeners)
|
374
364
|
- [Area Methods/Attributes](#area-methods-attributes)
|
375
365
|
- [Area Transform Matrix](#area-transform-matrix)
|
366
|
+
- [Area Composite Shape](#area-composite-shape)
|
376
367
|
- [Area Animation](#area-animation)
|
377
368
|
- [Smart Defaults and Conventions](#smart-defaults-and-conventions)
|
378
369
|
- [Custom Keywords](#custom-keywords)
|
@@ -544,7 +535,7 @@ gem install glimmer-dsl-libui
|
|
544
535
|
Or install via Bundler `Gemfile`:
|
545
536
|
|
546
537
|
```ruby
|
547
|
-
gem 'glimmer-dsl-libui', '~> 0.
|
538
|
+
gem 'glimmer-dsl-libui', '~> 0.8.0'
|
548
539
|
```
|
549
540
|
|
550
541
|
Test that installation worked by running the [Meta-Example](#examples):
|
@@ -773,7 +764,7 @@ Keyword(Args) | Properties | Listeners
|
|
773
764
|
|
774
765
|
All operations that could normally be called on `LibUI` can also be called on `Glimmer::LibUI`, but some have enhancements as detailed below.
|
775
766
|
|
776
|
-
- `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible
|
767
|
+
- `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible. When writing multi-threaded code, it is required to wrap all code interacting with GUI objects (like `window` or `button`) from another `Thread` with `Glimmer::LibUI::queue_main { ... }`. See [Glimmer Meta-Example](https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/examples/meta_example.rb) for an example of using `Glimmer::LibUI::queue_main { ... }` inside another `Thread`.
|
777
768
|
- `Glimmer::LibUI::timer(time_in_seconds=0.1, repeat: true, &block)`: calls block after time_in_seconds has elapsed, repeating indefinitely unless repeat is `false` or an `Integer` for finite number of repeats. Block can return `false` or `true` to override next repetition.
|
778
769
|
|
779
770
|
There are additional useful `Glimmer::LibUI` operations that are not found in `LibUI`, which mostly help if you would like to do advanced lower level [LibUI](https://github.com/kojix2/LibUI) programming:
|
@@ -1563,6 +1554,170 @@ You can set a `matrix`/`transform` on `area` directly to conveniently apply to a
|
|
1563
1554
|
|
1564
1555
|
Note that `area`, `path`, and nested shapes are all truly declarative, meaning they do not care about the ordering of calls to `fill`, `stroke`, and `transform`. Furthermore, any transform that is applied is reversed at the end of the block, so you never have to worry about the ordering of `transform` calls among different paths. You simply set a transform on the `path`s that need it and it is guaranteed to be called before all its content is drawn, and then undone afterwards to avoid affecting later paths. Matrix `transform` can be set on an entire `area` too, applying to all nested `path`s.
|
1565
1556
|
|
1557
|
+
#### Area Composite Shape
|
1558
|
+
|
1559
|
+
If you would like to build a composite shape that contains smaller sub-shapes, which would all get treated as a single unit,
|
1560
|
+
you can use the `shape` (or `composite_shape`) keyword, and wrap all the sub-shapes within the composite `shape`.
|
1561
|
+
|
1562
|
+
If you specify the `fill`, `stroke`, and `transform` at the `shape` level, they will get inherited by all sub-shapes that do not
|
1563
|
+
specify values for `fill`, `stroke`, or `transform` (though if they do, they override their parent's value).
|
1564
|
+
|
1565
|
+
When you use the `include?(x, y)` or `contain?(x, y)` method on a composite `shape`, it automatically includes all its aggregated shapes
|
1566
|
+
in the inclusion or containment check using the corresponding [PerfectShape](https://github.com/AndyObtiva/perfect-shape) object.
|
1567
|
+
|
1568
|
+
Example of a `cube` method-based custom keyword built using the composite `shape` keyword:
|
1569
|
+
|
1570
|
+
![glimmer-dsl-libui-mac-basic-composite-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
|
1571
|
+
|
1572
|
+
```ruby
|
1573
|
+
require 'glimmer-dsl-libui'
|
1574
|
+
|
1575
|
+
class BasicCompositeShape
|
1576
|
+
include Glimmer::LibUI::Application
|
1577
|
+
|
1578
|
+
body {
|
1579
|
+
window {
|
1580
|
+
title 'Basic Composite Shape'
|
1581
|
+
content_size 200, 225
|
1582
|
+
|
1583
|
+
@area = area {
|
1584
|
+
rectangle(0, 0, 200, 225) {
|
1585
|
+
fill :white
|
1586
|
+
}
|
1587
|
+
|
1588
|
+
7.times do |n|
|
1589
|
+
x_location = (rand*125).to_i%200 + (rand*15).to_i
|
1590
|
+
y_location = (rand*125).to_i%200 + (rand*15).to_i
|
1591
|
+
shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
|
1592
|
+
shape_size = 20+n
|
1593
|
+
|
1594
|
+
cube(
|
1595
|
+
location_x: x_location,
|
1596
|
+
location_y: y_location,
|
1597
|
+
rectangle_width: shape_size*2,
|
1598
|
+
rectangle_height: shape_size,
|
1599
|
+
cube_height: shape_size*2,
|
1600
|
+
background_color: shape_color,
|
1601
|
+
line_thickness: 2
|
1602
|
+
) { |the_shape|
|
1603
|
+
on_mouse_up do |area_mouse_event|
|
1604
|
+
# Change color on mouse up without dragging
|
1605
|
+
if @drag_shape.nil?
|
1606
|
+
background_color = [rand(255), rand(255), rand(255)]
|
1607
|
+
the_shape.fill = background_color
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
on_mouse_drag_start do |area_mouse_event|
|
1612
|
+
@drag_shape = the_shape
|
1613
|
+
@drag_x = area_mouse_event[:x]
|
1614
|
+
@drag_y = area_mouse_event[:y]
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
on_mouse_drag do |area_mouse_event|
|
1618
|
+
if @drag_shape && @drag_x && @drag_y
|
1619
|
+
drag_distance_width = area_mouse_event[:x] - @drag_x
|
1620
|
+
drag_distance_height = area_mouse_event[:y] - @drag_y
|
1621
|
+
@drag_shape.x += drag_distance_width
|
1622
|
+
@drag_shape.y += drag_distance_height
|
1623
|
+
@drag_x = area_mouse_event[:x]
|
1624
|
+
@drag_y = area_mouse_event[:y]
|
1625
|
+
end
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
on_mouse_drop do |area_mouse_event|
|
1629
|
+
@drag_shape = nil
|
1630
|
+
@drag_x = nil
|
1631
|
+
@drag_y = nil
|
1632
|
+
end
|
1633
|
+
}
|
1634
|
+
end
|
1635
|
+
|
1636
|
+
# this general area on_mouse_drag listener is needed to ensure that dragging a shape
|
1637
|
+
# outside of its boundaries would still move the dragged shape
|
1638
|
+
on_mouse_drag do |area_mouse_event|
|
1639
|
+
if @drag_shape && @drag_x && @drag_y
|
1640
|
+
drag_distance_width = area_mouse_event[:x] - @drag_x
|
1641
|
+
drag_distance_height = area_mouse_event[:y] - @drag_y
|
1642
|
+
@drag_shape.x += drag_distance_width
|
1643
|
+
@drag_shape.y += drag_distance_height
|
1644
|
+
@drag_x = area_mouse_event[:x]
|
1645
|
+
@drag_y = area_mouse_event[:y]
|
1646
|
+
end
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
on_mouse_drop do |area_mouse_event|
|
1650
|
+
@drag_shape = nil
|
1651
|
+
@drag_x = nil
|
1652
|
+
@drag_y = nil
|
1653
|
+
end
|
1654
|
+
}
|
1655
|
+
}
|
1656
|
+
}
|
1657
|
+
|
1658
|
+
# method-based custom shape using `shape` keyword as a composite shape containing nested shapes
|
1659
|
+
# that are declared with relative positioning
|
1660
|
+
def cube(location_x: 0,
|
1661
|
+
location_y: 0,
|
1662
|
+
rectangle_width: nil,
|
1663
|
+
rectangle_height: nil,
|
1664
|
+
cube_height: nil,
|
1665
|
+
background_color: :brown,
|
1666
|
+
line_thickness: 1,
|
1667
|
+
&content_block)
|
1668
|
+
default_size = 28
|
1669
|
+
rectangle_width ||= rectangle_height || cube_height || default_size
|
1670
|
+
rectangle_height ||= rectangle_width || cube_height || default_size
|
1671
|
+
cube_height ||= rectangle_width || rectangle_height || default_size
|
1672
|
+
foreground_color = [0, 0, 0, thickness: line_thickness]
|
1673
|
+
|
1674
|
+
# the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
|
1675
|
+
# like a cube containing polygons, a polyline, a rectangle, and a line
|
1676
|
+
# with the fill and stroke colors getting inherited by all children that do not specify them
|
1677
|
+
shape(location_x, location_y) { |the_shape|
|
1678
|
+
fill background_color
|
1679
|
+
stroke foreground_color
|
1680
|
+
|
1681
|
+
bottom = polygon(0, cube_height + rectangle_height / 2.0,
|
1682
|
+
rectangle_width / 2.0, cube_height,
|
1683
|
+
rectangle_width, cube_height + rectangle_height / 2.0,
|
1684
|
+
rectangle_width / 2.0, cube_height + rectangle_height) {
|
1685
|
+
# inherits fill property from parent shape if not set
|
1686
|
+
# inherits stroke property from parent shape if not set
|
1687
|
+
}
|
1688
|
+
body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
|
1689
|
+
# inherits fill property from parent shape if not set
|
1690
|
+
# stroke is overridden to ensure a different value from parent
|
1691
|
+
stroke thickness: 0
|
1692
|
+
}
|
1693
|
+
polyline(0, rectangle_height / 2.0 + cube_height,
|
1694
|
+
0, rectangle_height / 2.0,
|
1695
|
+
rectangle_width, rectangle_height / 2.0,
|
1696
|
+
rectangle_width, rectangle_height / 2.0 + cube_height) {
|
1697
|
+
# inherits stroke property from parent shape if not set
|
1698
|
+
}
|
1699
|
+
top = polygon(0, rectangle_height / 2.0,
|
1700
|
+
rectangle_width / 2.0, 0,
|
1701
|
+
rectangle_width, rectangle_height / 2.0,
|
1702
|
+
rectangle_width / 2.0, rectangle_height) {
|
1703
|
+
# inherits fill property from parent shape if not set
|
1704
|
+
# inherits stroke property from parent shape if not set
|
1705
|
+
}
|
1706
|
+
line(rectangle_width / 2.0, cube_height + rectangle_height,
|
1707
|
+
rectangle_width / 2.0, rectangle_height) {
|
1708
|
+
# inherits stroke property from parent shape if not set
|
1709
|
+
}
|
1710
|
+
|
1711
|
+
content_block&.call(the_shape)
|
1712
|
+
}
|
1713
|
+
end
|
1714
|
+
end
|
1715
|
+
|
1716
|
+
BasicCompositeShape.launch
|
1717
|
+
|
1718
|
+
|
1719
|
+
```
|
1720
|
+
|
1566
1721
|
#### Area Animation
|
1567
1722
|
|
1568
1723
|
If you need to animate `area` vector graphics, you just have to use the [`Glimmer::LibUI::timer`](#libui-operations) method along with making changes to shape attributes.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
@@ -25,6 +25,7 @@
|
|
25
25
|
- [Basic Transform](#basic-transform)
|
26
26
|
- [Basic Draw Text](#basic-draw-text)
|
27
27
|
- [Basic Code Area](#basic-code-area)
|
28
|
+
- [Basic Composite Shape](#basic-composite-shape)
|
28
29
|
|
29
30
|
## Basic Window
|
30
31
|
|
@@ -2344,36 +2345,20 @@ Mac | Windows | Linux
|
|
2344
2345
|
|
2345
2346
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
2346
2347
|
|
2347
|
-
|
2348
|
-
require 'glimmer-dsl-libui'
|
2348
|
+
## Basic Composite Shape
|
2349
2349
|
|
2350
|
-
|
2351
|
-
|
2352
|
-
|
2353
|
-
before_body do
|
2354
|
-
@code = <<~CODE
|
2355
|
-
# Greets target with greeting
|
2356
|
-
def greet(greeting: 'Hello', target: 'World')
|
2357
|
-
|
2358
|
-
puts "\#{greeting}, \#{target}!"
|
2359
|
-
end
|
2360
|
-
|
2361
|
-
greet
|
2362
|
-
greet(target: 'Robert')
|
2363
|
-
greet(greeting: 'Aloha')
|
2364
|
-
greet(greeting: 'Aloha', target: 'Nancy')
|
2365
|
-
greet(greeting: 'Howdy', target: 'Doodle')
|
2366
|
-
CODE
|
2367
|
-
end
|
2368
|
-
|
2369
|
-
body {
|
2370
|
-
window('Basic Code Area', 400, 300) {
|
2371
|
-
margined true
|
2372
|
-
|
2373
|
-
code_area(language: 'ruby', code: @code)
|
2374
|
-
}
|
2375
|
-
}
|
2376
|
-
end
|
2350
|
+
[examples/basic_composite_shape.rb](/examples/basic_composite_shape.rb)
|
2351
|
+
|
2352
|
+
Run with this command from the root of the project if you cloned the project:
|
2377
2353
|
|
2378
|
-
BasicCodeArea.launch
|
2379
2354
|
```
|
2355
|
+
ruby -r './lib/glimmer-dsl-libui' examples/basic_composite_shape.rb
|
2356
|
+
```
|
2357
|
+
|
2358
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
2359
|
+
|
2360
|
+
```
|
2361
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/basic_composite_shape.rb'"
|
2362
|
+
```
|
2363
|
+
|
2364
|
+
![glimmer-dsl-libui-mac-basic-composite-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
class BasicCompositeShape
|
4
|
+
include Glimmer::LibUI::Application
|
5
|
+
|
6
|
+
body {
|
7
|
+
window {
|
8
|
+
title 'Basic Composite Shape'
|
9
|
+
content_size 200, 225
|
10
|
+
|
11
|
+
@area = area {
|
12
|
+
rectangle(0, 0, 200, 225) {
|
13
|
+
fill :white
|
14
|
+
}
|
15
|
+
|
16
|
+
7.times do |n|
|
17
|
+
x_location = (rand*125).to_i%200 + (rand*15).to_i
|
18
|
+
y_location = (rand*125).to_i%200 + (rand*15).to_i
|
19
|
+
shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
|
20
|
+
shape_size = 20+n
|
21
|
+
|
22
|
+
cube(
|
23
|
+
location_x: x_location,
|
24
|
+
location_y: y_location,
|
25
|
+
rectangle_width: shape_size*2,
|
26
|
+
rectangle_height: shape_size,
|
27
|
+
cube_height: shape_size*2,
|
28
|
+
background_color: shape_color,
|
29
|
+
line_thickness: 2
|
30
|
+
) { |the_shape|
|
31
|
+
on_mouse_up do |area_mouse_event|
|
32
|
+
# Change color on mouse up without dragging
|
33
|
+
if @drag_shape.nil?
|
34
|
+
background_color = [rand(255), rand(255), rand(255)]
|
35
|
+
the_shape.fill = background_color
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
on_mouse_drag_start do |area_mouse_event|
|
40
|
+
@drag_shape = the_shape
|
41
|
+
@drag_x = area_mouse_event[:x]
|
42
|
+
@drag_y = area_mouse_event[:y]
|
43
|
+
end
|
44
|
+
|
45
|
+
on_mouse_drag do |area_mouse_event|
|
46
|
+
if @drag_shape && @drag_x && @drag_y
|
47
|
+
drag_distance_width = area_mouse_event[:x] - @drag_x
|
48
|
+
drag_distance_height = area_mouse_event[:y] - @drag_y
|
49
|
+
@drag_shape.x += drag_distance_width
|
50
|
+
@drag_shape.y += drag_distance_height
|
51
|
+
@drag_x = area_mouse_event[:x]
|
52
|
+
@drag_y = area_mouse_event[:y]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
on_mouse_drop do |area_mouse_event|
|
57
|
+
@drag_shape = nil
|
58
|
+
@drag_x = nil
|
59
|
+
@drag_y = nil
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# this general area on_mouse_drag listener is needed to ensure that dragging a shape
|
65
|
+
# outside of its boundaries would still move the dragged shape
|
66
|
+
on_mouse_drag do |area_mouse_event|
|
67
|
+
if @drag_shape && @drag_x && @drag_y
|
68
|
+
drag_distance_width = area_mouse_event[:x] - @drag_x
|
69
|
+
drag_distance_height = area_mouse_event[:y] - @drag_y
|
70
|
+
@drag_shape.x += drag_distance_width
|
71
|
+
@drag_shape.y += drag_distance_height
|
72
|
+
@drag_x = area_mouse_event[:x]
|
73
|
+
@drag_y = area_mouse_event[:y]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
on_mouse_drop do |area_mouse_event|
|
78
|
+
@drag_shape = nil
|
79
|
+
@drag_x = nil
|
80
|
+
@drag_y = nil
|
81
|
+
end
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
# method-based custom shape using `shape` keyword as a composite shape containing nested shapes
|
87
|
+
# that are declared with relative positioning
|
88
|
+
def cube(location_x: 0,
|
89
|
+
location_y: 0,
|
90
|
+
rectangle_width: nil,
|
91
|
+
rectangle_height: nil,
|
92
|
+
cube_height: nil,
|
93
|
+
background_color: :brown,
|
94
|
+
line_thickness: 1,
|
95
|
+
&content_block)
|
96
|
+
default_size = 28
|
97
|
+
rectangle_width ||= rectangle_height || cube_height || default_size
|
98
|
+
rectangle_height ||= rectangle_width || cube_height || default_size
|
99
|
+
cube_height ||= rectangle_width || rectangle_height || default_size
|
100
|
+
foreground_color = [0, 0, 0, thickness: line_thickness]
|
101
|
+
|
102
|
+
# the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
|
103
|
+
# like a cube containing polygons, a polyline, a rectangle, and a line
|
104
|
+
# with the fill and stroke colors getting inherited by all children that do not specify them
|
105
|
+
shape(location_x, location_y) { |the_shape|
|
106
|
+
fill background_color
|
107
|
+
stroke foreground_color
|
108
|
+
|
109
|
+
bottom = polygon(0, cube_height + rectangle_height / 2.0,
|
110
|
+
rectangle_width / 2.0, cube_height,
|
111
|
+
rectangle_width, cube_height + rectangle_height / 2.0,
|
112
|
+
rectangle_width / 2.0, cube_height + rectangle_height) {
|
113
|
+
# inherits fill property from parent shape if not set
|
114
|
+
# inherits stroke property from parent shape if not set
|
115
|
+
}
|
116
|
+
body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
|
117
|
+
# inherits fill property from parent shape if not set
|
118
|
+
# stroke is overridden to ensure a different value from parent
|
119
|
+
stroke thickness: 0
|
120
|
+
}
|
121
|
+
polyline(0, rectangle_height / 2.0 + cube_height,
|
122
|
+
0, rectangle_height / 2.0,
|
123
|
+
rectangle_width, rectangle_height / 2.0,
|
124
|
+
rectangle_width, rectangle_height / 2.0 + cube_height) {
|
125
|
+
# inherits stroke property from parent shape if not set
|
126
|
+
}
|
127
|
+
top = polygon(0, rectangle_height / 2.0,
|
128
|
+
rectangle_width / 2.0, 0,
|
129
|
+
rectangle_width, rectangle_height / 2.0,
|
130
|
+
rectangle_width / 2.0, rectangle_height) {
|
131
|
+
# inherits fill property from parent shape if not set
|
132
|
+
# inherits stroke property from parent shape if not set
|
133
|
+
}
|
134
|
+
line(rectangle_width / 2.0, cube_height + rectangle_height,
|
135
|
+
rectangle_width / 2.0, rectangle_height) {
|
136
|
+
# inherits stroke property from parent shape if not set
|
137
|
+
}
|
138
|
+
|
139
|
+
content_block&.call(the_shape)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
BasicCompositeShape.launch
|
145
|
+
|
data/examples/button_counter.rb
CHANGED
data/glimmer-dsl-libui.gemspec
CHANGED
Binary file
|
@@ -33,8 +33,7 @@ module Glimmer
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def interpret(parent, keyword, *args, &block)
|
36
|
-
|
37
|
-
keyword = @@inverted_keyword_aliases[keyword] || keyword
|
36
|
+
keyword = Glimmer::LibUI::ControlProxy::KEYWORD_ALIASES[keyword] || keyword
|
38
37
|
Glimmer::LibUI::ControlProxy.create(keyword, parent, args, &block)
|
39
38
|
end
|
40
39
|
|
@@ -42,6 +42,7 @@ module Glimmer
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def interpret(parent, keyword, *args, &block)
|
45
|
+
keyword = Glimmer::LibUI::Shape::KEYWORD_ALIASES[keyword] || keyword
|
45
46
|
args = [args] if args.size > 1 && Glimmer::LibUI::Shape.shape_class(keyword).parameters.size == 1
|
46
47
|
Glimmer::LibUI::Shape.create(keyword, parent, args, &block)
|
47
48
|
end
|
@@ -167,9 +167,9 @@ module Glimmer
|
|
167
167
|
end
|
168
168
|
|
169
169
|
def perfect_shape
|
170
|
-
|
171
|
-
if
|
172
|
-
draw_fill_mode,
|
170
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
171
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
172
|
+
draw_fill_mode, _ = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
173
173
|
shapes = children.map(&:perfect_shape)
|
174
174
|
new_shapes = []
|
175
175
|
shapes.each do |shape|
|
@@ -190,6 +190,10 @@ module Glimmer
|
|
190
190
|
@perfect_shape
|
191
191
|
end
|
192
192
|
|
193
|
+
def perfect_shape_dependencies
|
194
|
+
[draw_fill_mode, children.map(&:perfect_shape_dependencies)]
|
195
|
+
end
|
196
|
+
|
193
197
|
private
|
194
198
|
|
195
199
|
def build_control
|
@@ -35,10 +35,10 @@ module Glimmer
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def create(keyword, parent, args, &block)
|
38
|
-
|
38
|
+
control_proxy_class(keyword).new(keyword, parent, args, &block).tap {|c| control_proxies << c}
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
41
|
+
def control_proxy_class(keyword)
|
42
42
|
descendant_keyword_constant_map[keyword] || ControlProxy
|
43
43
|
end
|
44
44
|
|
@@ -96,7 +96,7 @@ module Glimmer
|
|
96
96
|
private
|
97
97
|
|
98
98
|
def add_aliases_to_keyword_constant_map(keyword_constant_map)
|
99
|
-
KEYWORD_ALIASES.each do |
|
99
|
+
KEYWORD_ALIASES.each do |alias_keyword, keyword|
|
100
100
|
keyword_constant_map[alias_keyword] = keyword_constant_map[keyword]
|
101
101
|
end
|
102
102
|
keyword_constant_map
|
@@ -106,8 +106,8 @@ module Glimmer
|
|
106
106
|
include DataBindable
|
107
107
|
|
108
108
|
KEYWORD_ALIASES = {
|
109
|
-
'
|
110
|
-
'
|
109
|
+
'message_box' => 'msg_box',
|
110
|
+
'message_box_error' => 'msg_box_error',
|
111
111
|
}
|
112
112
|
|
113
113
|
BOOLEAN_PROPERTIES = %w[
|
@@ -8,7 +8,7 @@ module Glimmer
|
|
8
8
|
|
9
9
|
def_delegators :perfect_shape,
|
10
10
|
:min_x, :min_y, :max_x, :max_y, :center_point, :center_x, :center_y
|
11
|
-
|
11
|
+
|
12
12
|
# Returns if shape contains point on the inside when outline is false (default)
|
13
13
|
# or if point is on the outline when outline is true
|
14
14
|
# distance_tolerance is used when outline is true to enable a fuzz factor in
|
@@ -22,6 +22,7 @@ module Glimmer
|
|
22
22
|
# or if shape includes point on the outline when stroked
|
23
23
|
def include?(*point)
|
24
24
|
if fill.empty?
|
25
|
+
# TODO check if distance_tolerance should be half the thickness in case it is checked against both sides of out and in
|
25
26
|
contain?(*point, outline: true, distance_tolerance: ((stroke[:thickness] || 1) - 1))
|
26
27
|
else
|
27
28
|
contain?(*point)
|
@@ -60,6 +61,13 @@ module Glimmer
|
|
60
61
|
def perfect_shape
|
61
62
|
# No Op
|
62
63
|
end
|
64
|
+
|
65
|
+
# Returns PerfectShape object dependencies to determine if the PerfectShape
|
66
|
+
# object changed or not for caching purposes.
|
67
|
+
# Every shape/path implements this uniquely for its own PerfectShape attribute dependencies
|
68
|
+
def perfect_shape_dependencies
|
69
|
+
# No Op
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
@@ -51,21 +51,25 @@ module Glimmer
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def perfect_shape
|
54
|
-
|
55
|
-
if
|
56
|
-
|
54
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
55
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
56
|
+
absolute_x_center, absolute_y_center, radius, start_angle, sweep, is_negative = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
57
57
|
sign = is_negative ? 1 : -1
|
58
58
|
start = is_negative ? (360 - start_angle) : -1*start_angle
|
59
59
|
extent = is_negative ? (360 - sweep) : -1*sweep
|
60
60
|
@perfect_shape = PerfectShape::Arc.new(
|
61
61
|
type: :open,
|
62
|
-
center_x:
|
62
|
+
center_x: absolute_x_center, center_y: absolute_y_center,
|
63
63
|
radius_x: radius, radius_y: radius,
|
64
64
|
start: start, extent: extent
|
65
65
|
)
|
66
66
|
end
|
67
67
|
@perfect_shape
|
68
68
|
end
|
69
|
+
|
70
|
+
def perfect_shape_dependencies
|
71
|
+
[absolute_x_center, absolute_y_center, radius, start_angle, sweep, is_negative]
|
72
|
+
end
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
@@ -62,13 +62,17 @@ module Glimmer
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def perfect_shape
|
65
|
-
|
66
|
-
if
|
67
|
-
|
68
|
-
@perfect_shape = PerfectShape::CubicBezierCurve.new(points: [
|
65
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
66
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
67
|
+
absolute_x, absolute_y, absolute_c1_x, absolute_c1_y, absolute_c2_x, absolute_c2_y, absolute_end_x, absolute_end_y = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
68
|
+
@perfect_shape = PerfectShape::CubicBezierCurve.new(points: [absolute_x, absolute_y, absolute_c1_x, absolute_c1_y, absolute_c2_x, absolute_c2_y, absolute_end_x, absolute_end_y].compact)
|
69
69
|
end
|
70
70
|
@perfect_shape
|
71
71
|
end
|
72
|
+
|
73
|
+
def perfect_shape_dependencies
|
74
|
+
[absolute_x, absolute_y, absolute_c1_x, absolute_c1_y, absolute_c2_x, absolute_c2_y, absolute_end_x, absolute_end_y]
|
75
|
+
end
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -51,13 +51,17 @@ module Glimmer
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def perfect_shape
|
54
|
-
|
55
|
-
if
|
56
|
-
|
57
|
-
@perfect_shape = PerfectShape::Circle.new(center_x:
|
54
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
55
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
56
|
+
absolute_x_center, absolute_y_center, radius = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
57
|
+
@perfect_shape = PerfectShape::Circle.new(center_x: absolute_x_center, center_y: absolute_y_center, radius: radius)
|
58
58
|
end
|
59
59
|
@perfect_shape
|
60
60
|
end
|
61
|
+
|
62
|
+
def perfect_shape_dependencies
|
63
|
+
[absolute_x_center, absolute_y_center, radius]
|
64
|
+
end
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Copyright (c) 2021-2022 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'glimmer/libui/shape'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
module LibUI
|
26
|
+
class Shape
|
27
|
+
class CompositeShape < Glimmer::LibUI::Shape
|
28
|
+
# TODO support nested shape properties that apply to all children
|
29
|
+
parameters :x, :y
|
30
|
+
parameter_defaults 0, 0
|
31
|
+
|
32
|
+
def draw(area_draw_params)
|
33
|
+
children.each do |child|
|
34
|
+
child_fill = child.fill
|
35
|
+
child_stroke = child.stroke
|
36
|
+
child_transform = child.transform
|
37
|
+
child.fill = fill if Glimmer::LibUI.blank_color?(child.fill)
|
38
|
+
child.stroke = stroke if Glimmer::LibUI.blank_color?(child.stroke)
|
39
|
+
child.transform = transform if child.transform.nil?
|
40
|
+
child.move_by(x, y)
|
41
|
+
begin
|
42
|
+
child.draw(area_draw_params)
|
43
|
+
rescue Exception => e
|
44
|
+
raise e
|
45
|
+
ensure
|
46
|
+
# restore original child attributes
|
47
|
+
child.move_by(-x, -y)
|
48
|
+
child.transform = child_transform
|
49
|
+
child.stroke = Glimmer::LibUI.blank_color?(child_stroke) ? Glimmer::LibUI.blank_color : child_stroke
|
50
|
+
child.fill = Glimmer::LibUI.blank_color?(child_fill) ? Glimmer::LibUI.blank_color : child_fill
|
51
|
+
end
|
52
|
+
end
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def transform(matrix = nil)
|
57
|
+
if matrix.nil?
|
58
|
+
@matrix
|
59
|
+
else
|
60
|
+
@matrix = matrix
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def move_by(x_delta, y_delta)
|
65
|
+
self.x += x_delta
|
66
|
+
self.y += y_delta
|
67
|
+
end
|
68
|
+
|
69
|
+
def contain?(*point, outline: false, distance_tolerance: 0)
|
70
|
+
children.any? { |child| child.contain?(*point, outline: outline, distance_tolerance: distance_tolerance) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def include?(*point)
|
74
|
+
children.any? { |child| child.include?(*point) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def relative_x(x)
|
78
|
+
self.x + x
|
79
|
+
end
|
80
|
+
|
81
|
+
def relative_y(y)
|
82
|
+
self.y + y
|
83
|
+
end
|
84
|
+
|
85
|
+
def relative_point(*point)
|
86
|
+
[relative_x(point.first), relative_y(point.last)]
|
87
|
+
end
|
88
|
+
|
89
|
+
def perfect_shape
|
90
|
+
perfect_shape_dependencies = [x, y, children.map(&:perfect_shape_dependencies)]
|
91
|
+
if perfect_shape_dependencies != @perfect_shape_dependencies
|
92
|
+
x, y, _ = @perfect_shape_dependencies = perfect_shape_dependencies
|
93
|
+
shapes = children.map(&:perfect_shape)
|
94
|
+
@perfect_shape = PerfectShape::CompositeShape.new(shapes: shapes)
|
95
|
+
end
|
96
|
+
@perfect_shape
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -58,10 +58,10 @@ module Glimmer
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def perfect_shape
|
61
|
-
|
62
|
-
if
|
63
|
-
|
64
|
-
path_shapes = [[
|
61
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
62
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
63
|
+
absolute_x, absolute_y, closed, draw_fill_mode, children = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
64
|
+
path_shapes = [[absolute_x, absolute_y]]
|
65
65
|
path_shapes += children.map(&:perfect_shape)
|
66
66
|
winding_rule = draw_fill_mode == :winding ? :wind_non_zero : :wind_even_odd
|
67
67
|
@perfect_shape = PerfectShape::Path.new(
|
@@ -73,6 +73,10 @@ module Glimmer
|
|
73
73
|
end
|
74
74
|
@perfect_shape
|
75
75
|
end
|
76
|
+
|
77
|
+
def perfect_shape_dependencies
|
78
|
+
[absolute_x, absolute_y, closed, parent.draw_fill_mode, children]
|
79
|
+
end
|
76
80
|
end
|
77
81
|
end
|
78
82
|
end
|
@@ -61,13 +61,17 @@ module Glimmer
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def perfect_shape
|
64
|
-
|
65
|
-
if
|
66
|
-
|
67
|
-
@perfect_shape = PerfectShape::Line.new(points: [
|
64
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
65
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
66
|
+
absolute_x, absolute_y, absolute_end_x, absolute_end_y = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
67
|
+
@perfect_shape = PerfectShape::Line.new(points: [absolute_x, absolute_y, absolute_end_x, absolute_end_y].compact)
|
68
68
|
end
|
69
69
|
@perfect_shape
|
70
70
|
end
|
71
|
+
|
72
|
+
def perfect_shape_dependencies
|
73
|
+
[absolute_x, absolute_y, absolute_end_x, absolute_end_y]
|
74
|
+
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -45,16 +45,20 @@ module Glimmer
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def perfect_shape
|
48
|
-
|
49
|
-
if
|
50
|
-
|
51
|
-
path_shapes = [
|
52
|
-
bezier_shape_points =
|
48
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
49
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
50
|
+
absolute_point_array = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
51
|
+
path_shapes = [absolute_point_array.first]
|
52
|
+
bezier_shape_points = absolute_point_array.drop(1).each.with_index.to_a.group_by {|pair| pair.last/3}.values.map {|arr| arr.map(&:first)}
|
53
53
|
path_shapes += bezier_shape_points.map { |points| PerfectShape::CubicBezierCurve.new(points: points) }
|
54
54
|
@perfect_shape = PerfectShape::Path.new(closed: false, shapes: path_shapes)
|
55
55
|
end
|
56
56
|
@perfect_shape
|
57
57
|
end
|
58
|
+
|
59
|
+
def perfect_shape_dependencies
|
60
|
+
absolute_point_array
|
61
|
+
end
|
58
62
|
end
|
59
63
|
end
|
60
64
|
end
|
@@ -46,15 +46,19 @@ module Glimmer
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def perfect_shape
|
49
|
-
|
50
|
-
if
|
51
|
-
|
52
|
-
path_shapes = [
|
53
|
-
path_shapes +=
|
49
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
50
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
51
|
+
absolute_point_array = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
52
|
+
path_shapes = [absolute_point_array.first]
|
53
|
+
path_shapes += absolute_point_array.drop(1).map { |point| PerfectShape::Line.new(points: [point]) }
|
54
54
|
@perfect_shape = PerfectShape::Path.new(closed: true, shapes: path_shapes)
|
55
55
|
end
|
56
56
|
@perfect_shape
|
57
57
|
end
|
58
|
+
|
59
|
+
def perfect_shape_dependencies
|
60
|
+
absolute_point_array
|
61
|
+
end
|
58
62
|
end
|
59
63
|
end
|
60
64
|
end
|
@@ -45,15 +45,19 @@ module Glimmer
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def perfect_shape
|
48
|
-
|
49
|
-
if
|
50
|
-
|
51
|
-
path_shapes = [
|
52
|
-
path_shapes +=
|
48
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
49
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
50
|
+
absolute_point_array = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
51
|
+
path_shapes = [absolute_point_array.first]
|
52
|
+
path_shapes += absolute_point_array.drop(1).map { |point| PerfectShape::Line.new(points: [point]) }
|
53
53
|
@perfect_shape = PerfectShape::Path.new(closed: false, shapes: path_shapes)
|
54
54
|
end
|
55
55
|
@perfect_shape
|
56
56
|
end
|
57
|
+
|
58
|
+
def perfect_shape_dependencies
|
59
|
+
absolute_point_array
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
@@ -39,13 +39,17 @@ module Glimmer
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def perfect_shape
|
42
|
-
|
43
|
-
if
|
44
|
-
|
45
|
-
@perfect_shape = PerfectShape::Rectangle.new(x:
|
42
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
43
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
44
|
+
absolute_x, absolute_y, width, height = @perfect_shape_dependencies = the_perfect_shape_dependencies
|
45
|
+
@perfect_shape = PerfectShape::Rectangle.new(x: absolute_x, y: absolute_y, width: width, height: height)
|
46
46
|
end
|
47
47
|
@perfect_shape
|
48
48
|
end
|
49
|
+
|
50
|
+
def perfect_shape_dependencies
|
51
|
+
[absolute_x, absolute_y, width, height]
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
@@ -41,13 +41,17 @@ module Glimmer
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def perfect_shape
|
44
|
-
|
45
|
-
if
|
46
|
-
@perfect_shape_dependencies =
|
44
|
+
the_perfect_shape_dependencies = perfect_shape_dependencies
|
45
|
+
if the_perfect_shape_dependencies != @perfect_shape_dependencies
|
46
|
+
@perfect_shape_dependencies = the_perfect_shape_dependencies
|
47
47
|
@perfect_shape = PerfectShape::Square.new(x: @perfect_shape_dependencies[0], y: @perfect_shape_dependencies[1], length: @perfect_shape_dependencies[2])
|
48
48
|
end
|
49
49
|
@perfect_shape
|
50
50
|
end
|
51
|
+
|
52
|
+
def perfect_shape_dependencies
|
53
|
+
[absolute_x, absolute_y, length]
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
data/lib/glimmer/libui/shape.rb
CHANGED
@@ -31,16 +31,19 @@ module Glimmer
|
|
31
31
|
class Shape
|
32
32
|
class << self
|
33
33
|
def exists?(keyword)
|
34
|
+
keyword = KEYWORD_ALIASES[keyword] || keyword
|
34
35
|
Shape.constants.include?(constant_symbol(keyword)) and
|
35
36
|
shape_class(keyword).respond_to?(:ancestors) and
|
36
37
|
shape_class(keyword).ancestors.include?(Shape)
|
37
38
|
end
|
38
39
|
|
39
40
|
def create(keyword, parent, args, &block)
|
41
|
+
keyword = KEYWORD_ALIASES[keyword] || keyword
|
40
42
|
shape_class(keyword).new(keyword, parent, args, &block)
|
41
43
|
end
|
42
44
|
|
43
45
|
def shape_class(keyword)
|
46
|
+
keyword = KEYWORD_ALIASES[keyword] || keyword
|
44
47
|
Shape.const_get(constant_symbol(keyword))
|
45
48
|
end
|
46
49
|
|
@@ -61,6 +64,7 @@ module Glimmer
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def constant_symbol(keyword)
|
67
|
+
keyword = KEYWORD_ALIASES[keyword] || keyword
|
64
68
|
"#{keyword.camelcase(:upper)}".to_sym
|
65
69
|
end
|
66
70
|
end
|
@@ -69,10 +73,15 @@ module Glimmer
|
|
69
73
|
include PerfectShaped
|
70
74
|
include DataBindable
|
71
75
|
|
76
|
+
KEYWORD_ALIASES = {
|
77
|
+
'shape' => 'composite_shape',
|
78
|
+
}
|
79
|
+
|
72
80
|
attr_reader :parent, :args, :keyword, :block, :content_added
|
73
81
|
alias content_added? content_added
|
74
82
|
|
75
83
|
def initialize(keyword, parent, args, &block)
|
84
|
+
keyword = KEYWORD_ALIASES[keyword] || keyword
|
76
85
|
@keyword = keyword
|
77
86
|
@parent = parent
|
78
87
|
@args = args
|
@@ -129,6 +138,10 @@ module Glimmer
|
|
129
138
|
def path_proxy
|
130
139
|
find_parent_in_ancestors { |parent| parent.nil? || parent.is_a?(ControlProxy::PathProxy) }
|
131
140
|
end
|
141
|
+
|
142
|
+
def composite_shape
|
143
|
+
find_parent_in_ancestors { |parent| parent.nil? || parent.is_a?(CompositeShape) }
|
144
|
+
end
|
132
145
|
|
133
146
|
def fill(*args)
|
134
147
|
path_proxy.fill(*args)
|
@@ -148,6 +161,125 @@ module Glimmer
|
|
148
161
|
alias transform= transform
|
149
162
|
alias set_transform transform
|
150
163
|
|
164
|
+
def absolute_x
|
165
|
+
if composite_shape
|
166
|
+
composite_shape.relative_x(x)
|
167
|
+
else
|
168
|
+
x
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def absolute_y
|
173
|
+
if composite_shape
|
174
|
+
composite_shape.relative_y(y)
|
175
|
+
else
|
176
|
+
y
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def absolute_point_array
|
181
|
+
# TODO Consider moving this method into a module mixed into all shapes having point_array
|
182
|
+
return unless respond_to?(:point_array)
|
183
|
+
|
184
|
+
point_array = self.point_array || []
|
185
|
+
point_array = PerfectShape::MultiPoint.normalize_point_array(point_array)
|
186
|
+
point_array.map do |point|
|
187
|
+
if composite_shape
|
188
|
+
composite_shape.relative_point(*point)
|
189
|
+
else
|
190
|
+
point
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def absolute_x_center
|
196
|
+
# TODO Consider moving this method into a module mixed into all shapes having x_center
|
197
|
+
return unless respond_to?(:x_center)
|
198
|
+
|
199
|
+
if composite_shape
|
200
|
+
composite_shape.relative_x(x_center)
|
201
|
+
else
|
202
|
+
x_center
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def absolute_y_center
|
207
|
+
# TODO Consider moving this method into a module mixed into all shapes having y_center
|
208
|
+
return unless respond_to?(:y_center)
|
209
|
+
|
210
|
+
if composite_shape
|
211
|
+
composite_shape.relative_y(y_center)
|
212
|
+
else
|
213
|
+
y_center
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def absolute_c1_x
|
218
|
+
# TODO Consider moving this method into a module mixed into all shapes having c1_x
|
219
|
+
return unless respond_to?(:c1_x)
|
220
|
+
|
221
|
+
if composite_shape
|
222
|
+
composite_shape.relative_x(c1_x)
|
223
|
+
else
|
224
|
+
c1_x
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def absolute_c1_y
|
229
|
+
# TODO Consider moving this method into a module mixed into all shapes having c1_y
|
230
|
+
return unless respond_to?(:c1_y)
|
231
|
+
|
232
|
+
if composite_shape
|
233
|
+
composite_shape.relative_x(c1_y)
|
234
|
+
else
|
235
|
+
c1_y
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def absolute_c2_x
|
240
|
+
# TODO Consider moving this method into a module mixed into all shapes having c2_x
|
241
|
+
return unless respond_to?(:c2_x)
|
242
|
+
|
243
|
+
if composite_shape
|
244
|
+
composite_shape.relative_x(c2_x)
|
245
|
+
else
|
246
|
+
c2_x
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def absolute_c2_y
|
251
|
+
# TODO Consider moving this method into a module mixed into all shapes having c2_y
|
252
|
+
return unless respond_to?(:c2_y)
|
253
|
+
|
254
|
+
if composite_shape
|
255
|
+
composite_shape.relative_x(c2_y)
|
256
|
+
else
|
257
|
+
c2_y
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def absolute_end_x
|
262
|
+
# TODO Consider moving this method into a module mixed into all shapes having end_x
|
263
|
+
return unless respond_to?(:end_x)
|
264
|
+
|
265
|
+
if composite_shape
|
266
|
+
composite_shape.relative_x(end_x)
|
267
|
+
else
|
268
|
+
end_x
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def absolute_end_y
|
273
|
+
# TODO Consider moving this method into a module mixed into all shapes having end_y
|
274
|
+
return unless respond_to?(:end_y)
|
275
|
+
|
276
|
+
if composite_shape
|
277
|
+
composite_shape.relative_x(end_y)
|
278
|
+
else
|
279
|
+
end_y
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
151
283
|
def can_handle_listener?(listener_name)
|
152
284
|
area_proxy.can_handle_listener?(listener_name)
|
153
285
|
end
|
@@ -181,7 +313,16 @@ module Glimmer
|
|
181
313
|
super
|
182
314
|
end
|
183
315
|
end
|
184
|
-
|
316
|
+
|
317
|
+
# indicates if nested directly under area or on_draw event (having an implicit path not an explicit path parent)
|
318
|
+
def implicit_path?
|
319
|
+
@implicit_path ||= !!(
|
320
|
+
@parent.is_a?(Glimmer::LibUI::ControlProxy::AreaProxy) ||
|
321
|
+
@parent.is_a?(Glimmer::LibUI::Shape::CompositeShape) ||
|
322
|
+
(@parent.nil? && Glimmer::LibUI::ControlProxy::AreaProxy.current_area_draw_params)
|
323
|
+
)
|
324
|
+
end
|
325
|
+
|
185
326
|
private
|
186
327
|
|
187
328
|
def build_control
|
@@ -189,11 +330,6 @@ module Glimmer
|
|
189
330
|
@parent = Glimmer::LibUI::ControlProxy::PathProxy.new('path', @parent, [], &block)
|
190
331
|
end
|
191
332
|
|
192
|
-
# indicates if nested directly under area or on_draw event (having an implicit path not an explicit path parent)
|
193
|
-
def implicit_path?
|
194
|
-
@implicit_path ||= !!(@parent.is_a?(Glimmer::LibUI::ControlProxy::AreaProxy) || (@parent.nil? && Glimmer::LibUI::ControlProxy::AreaProxy.current_area_draw_params))
|
195
|
-
end
|
196
|
-
|
197
333
|
def dynamic?
|
198
334
|
((@parent.nil? || (@parent.is_a?(ControlProxy::PathProxy) && @parent.parent_proxy.nil?)) && Glimmer::LibUI::ControlProxy::AreaProxy.current_area_draw_params)
|
199
335
|
end
|
data/lib/glimmer/libui.rb
CHANGED
@@ -104,6 +104,21 @@ module Glimmer
|
|
104
104
|
result.merge!(options) if options
|
105
105
|
result
|
106
106
|
end
|
107
|
+
|
108
|
+
# returns whether the value represents no color (blank) or a present color
|
109
|
+
# when a path is first built, it has a blank color
|
110
|
+
def blank_color?(value)
|
111
|
+
value.nil? ||
|
112
|
+
(value.respond_to?(:empty?) && value.empty?) ||
|
113
|
+
(value.is_a?(Array) && value.compact.empty?) ||
|
114
|
+
(value.is_a?(Hash) && value.values.compact.empty?)
|
115
|
+
end
|
116
|
+
|
117
|
+
# returns a representation of a blank color
|
118
|
+
# when a path is first built, it has a blank color
|
119
|
+
def blank_color
|
120
|
+
[{}]
|
121
|
+
end
|
107
122
|
|
108
123
|
def hex_to_rgb(value)
|
109
124
|
if value.is_a?(String)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-libui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glimmer
|
@@ -301,7 +301,7 @@ description: Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development G
|
|
301
301
|
syntax that visually maps to GUI control hierarchy, convention over configuration
|
302
302
|
via smart defaults, automation of low-level details, requiring the least amount
|
303
303
|
of syntax possible to build GUI, bidirectional data-binding, and custom keyword
|
304
|
-
support.
|
304
|
+
support. If you liked Shoes, You'll love Glimmer!
|
305
305
|
email: andy.am@gmail.com
|
306
306
|
executables:
|
307
307
|
- girb
|
@@ -331,6 +331,7 @@ files:
|
|
331
331
|
- examples/basic_button.rb
|
332
332
|
- examples/basic_child_window.rb
|
333
333
|
- examples/basic_code_area.rb
|
334
|
+
- examples/basic_composite_shape.rb
|
334
335
|
- examples/basic_draw_text.rb
|
335
336
|
- examples/basic_draw_text2.rb
|
336
337
|
- examples/basic_entry.rb
|
@@ -533,6 +534,7 @@ files:
|
|
533
534
|
- lib/glimmer/libui/shape/arc.rb
|
534
535
|
- lib/glimmer/libui/shape/bezier.rb
|
535
536
|
- lib/glimmer/libui/shape/circle.rb
|
537
|
+
- lib/glimmer/libui/shape/composite_shape.rb
|
536
538
|
- lib/glimmer/libui/shape/figure.rb
|
537
539
|
- lib/glimmer/libui/shape/line.rb
|
538
540
|
- lib/glimmer/libui/shape/polybezier.rb
|
@@ -567,7 +569,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
567
569
|
- !ruby/object:Gem::Version
|
568
570
|
version: '0'
|
569
571
|
requirements: []
|
570
|
-
rubygems_version: 3.
|
572
|
+
rubygems_version: 3.2.22
|
571
573
|
signing_key:
|
572
574
|
specification_version: 4
|
573
575
|
summary: Glimmer DSL for LibUI
|