fxruby-enhancement 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 815c35e0699b8a7cb0bc90b27f21789751af71fd
4
+ data.tar.gz: c9cc4a024a7493888bc3e27ec96a319e1b8a1a63
5
+ SHA512:
6
+ metadata.gz: 5e6d8150e9bb259e6bf9b12c63a1e874e6ebcc9ead1594cfc5cf0fd0f12d307e712bdfbe6cd92ba0ec68cfb98c8b0847ddc0430c63fe1337b0c756436a0ce304
7
+ data.tar.gz: 36efe40ce1f47ab9d7a4623ede142171856c4648e45ba1a7c51bdfb67151774d7d35c615c0d7b57fa833a8fd4f0167f3ff637ed3ae3288cd17fc60e1c9230a41
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "fxruby"]
2
+ path = fxruby
3
+ url = git@github.com:larskanis/fxruby.git
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.0
data/.semver ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 2
5
+ :special: ''
6
+ :metadata: ''
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "semver2", "~> 3"
4
+ gem "fxruby", "~> 1.6"
5
+ gem "awesome_print", ">= 1"
6
+ gem "json", "~> 2.0", ">= 2.0.2"
7
+ gem "queue_ding", "~> 0"
8
+
9
+ group :development do
10
+ gem "rspec", "~> 3"
11
+ gem "yard", "~> 0.7"
12
+ gem "rdoc", "~> 5"
13
+ gem "bundler", "~> 1.0"
14
+ gem "juwelier", "~> 2"
15
+ gem "simplecov", ">= 0"
16
+ gem "pry", ">= 0"
17
+ gem "pry-byebug", ">= 3"
18
+ gem "pry-doc", ">= 0"
19
+ gem "pry-remote", ">= 0"
20
+ gem "pry-rescue", ">= 1"
21
+ gem "pry-stack_explorer", ">= 0"
22
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,131 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.4.0)
5
+ aquarium (0.5.1)
6
+ awesome_print (1.7.0)
7
+ binding_of_caller (0.7.2)
8
+ debug_inspector (>= 0.0.1)
9
+ builder (3.2.2)
10
+ byebug (9.0.6)
11
+ coderay (1.1.1)
12
+ debug_inspector (0.0.2)
13
+ descendants_tracker (0.0.4)
14
+ thread_safe (~> 0.3, >= 0.3.1)
15
+ diff-lcs (1.2.5)
16
+ docile (1.1.5)
17
+ faraday (0.9.2)
18
+ multipart-post (>= 1.2, < 3)
19
+ fxruby (1.6.34)
20
+ mini_portile (~> 0.6)
21
+ git (1.3.0)
22
+ github_api (0.14.5)
23
+ addressable (~> 2.4.0)
24
+ descendants_tracker (~> 0.0.4)
25
+ faraday (~> 0.8, < 0.10)
26
+ hashie (>= 3.4)
27
+ oauth2 (~> 1.0)
28
+ hashie (3.4.6)
29
+ highline (1.7.8)
30
+ interception (0.5)
31
+ json (2.0.2)
32
+ juwelier (2.3.4)
33
+ builder
34
+ bundler (>= 1.13)
35
+ git (>= 1.2.5)
36
+ github_api
37
+ highline (>= 1.6.15)
38
+ nokogiri (>= 1.5.10)
39
+ psych (~> 2.2)
40
+ rake
41
+ rdoc
42
+ semver2
43
+ jwt (1.5.6)
44
+ method_source (0.8.2)
45
+ mini_portile (0.6.2)
46
+ mini_portile2 (2.1.0)
47
+ multi_json (1.12.1)
48
+ multi_xml (0.6.0)
49
+ multipart-post (2.0.0)
50
+ nokogiri (1.7.0.1)
51
+ mini_portile2 (~> 2.1.0)
52
+ oauth2 (1.3.0)
53
+ faraday (>= 0.8, < 0.11)
54
+ jwt (~> 1.0)
55
+ multi_json (~> 1.3)
56
+ multi_xml (~> 0.5)
57
+ rack (>= 1.2, < 3)
58
+ pry (0.10.4)
59
+ coderay (~> 1.1.0)
60
+ method_source (~> 0.8.1)
61
+ slop (~> 3.4)
62
+ pry-byebug (3.4.2)
63
+ byebug (~> 9.0)
64
+ pry (~> 0.10)
65
+ pry-doc (0.10.0)
66
+ pry (~> 0.9)
67
+ yard (~> 0.9)
68
+ pry-remote (0.1.8)
69
+ pry (~> 0.9)
70
+ slop (~> 3.0)
71
+ pry-rescue (1.4.4)
72
+ interception (>= 0.5)
73
+ pry
74
+ pry-stack_explorer (0.4.9.2)
75
+ binding_of_caller (>= 0.7)
76
+ pry (>= 0.9.11)
77
+ psych (2.2.2)
78
+ queue_ding (0.1.5)
79
+ aquarium (~> 0)
80
+ json (~> 2.0, >= 2.0.2)
81
+ semver2 (~> 3)
82
+ rack (2.0.1)
83
+ rake (12.0.0)
84
+ rdoc (5.0.0)
85
+ rspec (3.5.0)
86
+ rspec-core (~> 3.5.0)
87
+ rspec-expectations (~> 3.5.0)
88
+ rspec-mocks (~> 3.5.0)
89
+ rspec-core (3.5.4)
90
+ rspec-support (~> 3.5.0)
91
+ rspec-expectations (3.5.0)
92
+ diff-lcs (>= 1.2.0, < 2.0)
93
+ rspec-support (~> 3.5.0)
94
+ rspec-mocks (3.5.0)
95
+ diff-lcs (>= 1.2.0, < 2.0)
96
+ rspec-support (~> 3.5.0)
97
+ rspec-support (3.5.0)
98
+ semver2 (3.4.2)
99
+ simplecov (0.12.0)
100
+ docile (~> 1.1.0)
101
+ json (>= 1.8, < 3)
102
+ simplecov-html (~> 0.10.0)
103
+ simplecov-html (0.10.0)
104
+ slop (3.6.0)
105
+ thread_safe (0.3.5)
106
+ yard (0.9.7)
107
+
108
+ PLATFORMS
109
+ ruby
110
+
111
+ DEPENDENCIES
112
+ awesome_print (>= 1)
113
+ bundler (~> 1.0)
114
+ fxruby (~> 1.6)
115
+ json (~> 2.0, >= 2.0.2)
116
+ juwelier (~> 2)
117
+ pry
118
+ pry-byebug (>= 3)
119
+ pry-doc
120
+ pry-remote
121
+ pry-rescue (>= 1)
122
+ pry-stack_explorer
123
+ queue_ding (~> 0)
124
+ rdoc (~> 5)
125
+ rspec (~> 3)
126
+ semver2 (~> 3)
127
+ simplecov
128
+ yard (~> 0.7)
129
+
130
+ BUNDLED WITH
131
+ 1.13.6
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Fred Mitchell
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.
data/README.org ADDED
@@ -0,0 +1,861 @@
1
+ #+OPTIONS: broken-links:mark
2
+ * Table of Contents :TOC_5_gh:
3
+ - [[#fxruby-enhancement][fxruby-enhancement]]
4
+ - [[#introduction][Introduction]]
5
+ - [[#installation][Installation]]
6
+ - [[#documentation][Documentation]]
7
+ - [[#in-general][In General]]
8
+ - [[#execution-phases-of-fxruby-enhancement][Execution Phases of fxruby-enhancement]]
9
+ - [[#declarative][Declarative]]
10
+ - [[#fxruby-instantiation][FXRuby Instantiation]]
11
+ - [[#fox-toolkit-instantiation][FOX Toolkit instantiation]]
12
+ - [[#events-from-other-threads][Events from other Threads]]
13
+ - [[#the-queue_ding-queues][The Queue_Ding Queues]]
14
+ - [[#enhancementingress][Enhancement.ingress]]
15
+ - [[#enhancementegress][Enhancement.egress]]
16
+ - [[#api--dsl][API & DSL]]
17
+ - [[#ref-refc-and-tagging-your-objects][ref(), refc() and tagging your objects]]
18
+ - [[#fox_component-and-fox_instance][fox_component and fox_instance]]
19
+ - [[#fx_app][fx_app]]
20
+ - [[#instance][instance]]
21
+ - [[#ingress_handler][ingress_handler]]
22
+ - [[#deferred_setup][deferred_setup]]
23
+ - [[#mapping-between-fx_-declarations-and-the-fx-fxruby-objects][Mapping between fx_* declarations and the FX* FXRuby objects]]
24
+ - [[#bindingfx][binding.fx]]
25
+ - [[#examples][Examples]]
26
+ - [[#hello-world-example-full-the-enhancement-way][Hello World example (full) the Enhancement Way]]
27
+ - [[#hello-world-the-old-fxruby-way][Hello World the old fxruby way:]]
28
+ - [[#bouncing-ball-example-full][Bouncing Ball example (full):]]
29
+ - [[#bouncing-ball-the-old-fxruby-way][Bouncing Ball the old fxruby way:]]
30
+ - [[#release-notes][Release Notes]]
31
+ - [[#known-issues][Known Issues]]
32
+ - [[#contributing-to-fxruby-enhancement][Contributing to fxruby-enhancement]]
33
+ - [[#copyright-and-licensing][Copyright and Licensing]]
34
+ - [[#the-junkyard--scratchpad][The Junkyard / Scratchpad]]
35
+ - [[#genesis-of-the-meta-meta-programming-whereby-brain-goes-boom][Genesis of the meta-meta programming, whereby brain goes boom]]
36
+
37
+ * fxruby-enhancement
38
+ ** Introduction
39
+ The fxruby library is an excellent wrapper for the FOX toolkit.
40
+ However, it reflects the C++-ness of FOX, rather than being more
41
+ Ruby-like. As such, creating composed objects with it tends to be
42
+ rather ugly and cumbersome. For every new component you create with
43
+ fxruby, you are handed back a reference to that object, which you'll
44
+ need to store somewhere. And then all the subsequent child objects
45
+ will need to be passed pointers to the parent objects.
46
+
47
+ So, if you need to redo a layout, it becomes a messy exercise.
48
+
49
+ fxruby-enhancement makes this a snap to do. You simply declare
50
+ your GUI arrangement in a nested fashion. fxruby-enhancement will
51
+ take care of passing parents to the nested children, and other issues
52
+ as well. You can now focus on creating a good GUI layout that you
53
+ can change on the fly without much fuss and bother.
54
+
55
+ fxruby-enhancement is basically a DSL of sorts, and every effort has
56
+ been taken to make it intuitive to use. Once you get the hang of it,
57
+ you should be able to look at the FXRuby API documentation and infer
58
+ the DSL construct for fxruby-enhancement. Please also see the many
59
+ [[file:examples][examples]].
60
+
61
+ ** Installation
62
+ To install the gem from commandline:
63
+
64
+ #+begin_src bash
65
+ gem install fxruby-enhancement
66
+ #+end_src
67
+
68
+ In your Gemfile:
69
+
70
+ #+begin_src ruby
71
+ gem "fxruby-enhancement", "~> 0"
72
+ #+end_src
73
+
74
+ fxruby-enhacement depends on fxruby version 1.6, and
75
+ will automatically include it. However fxruby has a c-extension
76
+ that must compile properly on your system. Normally, this is not
77
+ a concern, but it is something to be aware of.
78
+
79
+ ** Documentation
80
+ *** In General
81
+ fxruby-enhancement (which we will refer to as "Enhancement" from time
82
+ to time) makes use of the singleton pattern in Ruby. There is basically
83
+ no need to declare subclases off of most FXRuby classes. This is a very
84
+ C++ish way, and the way the C++ Fox Toolkit works. It will make most
85
+ hard-core Rubyists gnash their teeth.
86
+
87
+ Here, we do away with all of it. Also, the SEL_x variables -- which maps
88
+ to the C++ #defines of the same -- is replaced with method declarations
89
+ of the nature of sel_x -- just the lowercase version of the same. For instance,
90
+ in the straight fxruby, you would have to do something like:
91
+
92
+ #+begin_src ruby
93
+ @canvas.connect(SEL_PAINT) { |sender, sel, evt|
94
+ FXDCWindow.new(sender, evt) { |dc|
95
+ dc.drawImage(@backBuffer, 0, 0)
96
+ }
97
+ #+end_srcv
98
+
99
+ but with Enhancement, you do it thusly:
100
+
101
+ #+begin_src ruby
102
+ instance { |c|
103
+ c.sel_paint { |sender, sel, event|
104
+ FXDCWindow.new(sender, event) { |dc|
105
+ dc.drawImage(ref(:back_buffer), 0, 0)
106
+ }
107
+ }
108
+ }
109
+ #+end_src
110
+
111
+ And here we illustrate something else, the instance declaration. Why do
112
+ we do it this way? Because Enhancement is multi-phase. First, we declare
113
+ the GUI layout with Enhancement. At this time, none of the underlying
114
+ FXRuby objects exist yet, but need to be referenced anyway. So we defer
115
+ that part where references need to be resolved to the instance claus,
116
+ which, as you can well imagine, means the FXRuby object instances have
117
+ been instantiated.
118
+
119
+ You will also note the use of the ref clause, as in:
120
+ #+begin_src ruby
121
+ dc.drawImage(ref(:back_buffer), 0, 0)
122
+ #+end_src
123
+
124
+ When the :back_buffer object was declared, it was done thusly:
125
+ #+begin_src ruby
126
+ fx_image(:back_buffer) { opts IMAGE_KEEP }
127
+ #+end_src
128
+
129
+ So when the actual FXImage object is instantiated, it is associated to
130
+ the :back_buffer tag, which then is found by ref() and can be used
131
+ anywhere in the instantiation phase.
132
+ *** Execution Phases of fxruby-enhancement
133
+ This represents the work flow, in the order stated:
134
+ | State | Description |
135
+ |---------------------------+------------------------------------------------------------------------------------------------------------------------------|
136
+ | Declarative | The basic GUI layout is declared by the DSL, but it is not instantiated yet. |
137
+ | FXRuby instantiation | All the basic underlying FXRuby object are instantiatied, but the foundational FOX Toolkit Objects are not instantiated yet. |
138
+ | FOX Toolkit instantiation | The FOX Toolkit C++-level objects are now alive and kicking. |
139
+
140
+ **** Declarative
141
+ This phase, under the proverbial hood, ceates the component objects,
142
+ which are just place-holders for the underlying FXRuby objects.
143
+
144
+ When the FXRuby object is created, it is assigned to its place holder
145
+ component object, and can be references as comp.inst. In most cases,
146
+ you will almost never need to touch the component objects directly.
147
+ **** FXRuby Instantiation
148
+ During the FXRuby instantiantion stage, all of the FXRuby
149
+ objects are instantiated and stored in their respective
150
+ component objects. If they are tagged, the instantiated
151
+ object may be referenced with ref(), and the component
152
+ object itself may be referenced via refc(). There is almost
153
+ never a case where you would need to go after the component
154
+ object directly.
155
+ **** FOX Toolkit instantiation
156
+ All of the FOX Toolkit C++ objects, resources, etc. that
157
+ correspond to the FXRuby objects are now set up, and activated.
158
+ With the all-important "show PLACEMENT_SCREEN" command, the
159
+ FOX GUI should now be visible.
160
+ *** Events from other Threads
161
+ In handling interfacing to databases, AMQPs like RabbitMQ,
162
+ network connections, or just about anything else that might otherwise
163
+ slow down the GUI (Fox) thread and make it non-responsive, there needs
164
+ to be a clean way to get data into and out of the GUI thread.
165
+
166
+ Fox provides some mechanisms specifically for sockets or system-level IO,
167
+ but these are too specific, and would require some awkard workarounds to
168
+ make them work in the general context.
169
+
170
+ And so we provide a means to accomplish that in a clean -- to you, anyway --
171
+ manner. We make use of queue_ding queues for passing messages into and out of
172
+ the FXRuby (and therefore FXRuby Enhancement) space. This will allow you to
173
+ keep the GUI thread responsive and also to maintain a seperation of concerns.
174
+ **** The Queue_Ding Queues
175
+ [[ttps://github.com/flajann2/queue_ding][Queue Ding]] is an enhancement for doing queing across threads in Ruby,
176
+ and we offer it here to allow external events to be funneled into and
177
+ out of the Fox GUI thread. Usage is easy and straightforard. When
178
+ removing entries from Queue Ding using #next, the queue will block until
179
+ the next entry arrives. Since Queue Ding is really derived from ::Array,
180
+ you may also do thing like #empty? to check to see if entries are availabe
181
+ to avoid blocking.
182
+ ***** Enhancement.ingress
183
+ To get messages objects into fxruby_enhacement, simply #push or #<<
184
+ it into the queue as shown:
185
+ #+begin_src ruby
186
+ Enhancement.ingress << [:some_tag, some_payload]
187
+ #+end_src
188
+
189
+ In the DSL, you must set up a handler for the ingress,
190
+ #+begin_src ruby
191
+ ingress_handler :status do |tag, payload|
192
+ puts "received #{tag} => #{payload}"
193
+ end
194
+ #+end_src
195
+
196
+ And so your handler will most likely act as a dispatcher
197
+ for the payloads received. For example:
198
+ #+begin_src ruby
199
+ ingress_handler :status do |tag, logline|
200
+ puts "received #{tag} => #{payload}"
201
+ case tag
202
+ when :log_info
203
+ ref(:logging_info).appendItem logline
204
+ when :log_error
205
+ ref(:logging_error).appendItem logline
206
+ end
207
+ end
208
+ #+end_src
209
+
210
+ ***** Enhancement.egress
211
+ Wnen your Fox application needs to send a message to other
212
+ listening threads, You simply push your payload onto the egress queue
213
+ thusly:
214
+ #+begin_src ruby
215
+ Enhancement.egress << [:button_clicked, "I was clicked!"]
216
+ #+end_src
217
+
218
+ and your Ruby thread external to Fox would simply do:
219
+ #+begin_src
220
+ #+end_src
221
+
222
+ **** API & DSL
223
+ ***** ref(), refc() and tagging your objects
224
+ In an effort to eliminate the fuss and bother with
225
+ scoping issues and object reference, ref(:some_tag) will
226
+ retrive the FXRuby instance object so tagged with :some_tag.
227
+
228
+ You may have anonymous, i.e., untagged objects, and those will
229
+ not be findable by ref(). It is not necessary to tag all objects,
230
+ either.
231
+
232
+ refc() is similar to ref(), except it retrives the underlying
233
+ component object insted. Indeed, the following are equivalent
234
+ operations:
235
+ #+begin_src ruby
236
+ ref(:some_tag)
237
+ refc(:some_tag).inst
238
+ #+end_src
239
+
240
+ There may be some edge cases where you might want to
241
+ reference the underlying component ahead of FXRuby object
242
+ instaniation, but in the vast majority of the cases, it should
243
+ be unnecessary. My goal is to enable to do what you need, not
244
+ to restrict you. You may need it for debugging, etc.
245
+
246
+ Underlying, the component object is really a subclass of OpenScript.
247
+ While you may like to stuff some additional data there,
248
+ this is frowned upon because it might conflict with Enhancement.
249
+ If you have a need for this, please do a issue in GitHub.
250
+
251
+ ***** fox_component and fox_instance
252
+ fox_component and fox_instance are roughly the
253
+ equivalent of refc() and ref(), respecively. The
254
+ difference mainly being that fox_component does no
255
+ sanity checking, and is therefore slightly faster.
256
+
257
+ At some point, they may be merged, but for now don't
258
+ count on it.
259
+
260
+ To initialize and run your app, you customairly do the
261
+ following:
262
+ #+begin_src ruby
263
+ fox_component :app do |app|
264
+ app.launch
265
+ end
266
+ #+end_src
267
+
268
+ Which presumes your fx_app declaration was tagged with
269
+ :app as follows:
270
+ #+begin_src ruby
271
+ fx_app :app do
272
+ app_name "Your Amazingly Cool Application"
273
+ vendor_name "YouDaMan"
274
+ ...
275
+ end
276
+ #+end_src
277
+
278
+ This is the only time you will reference the component
279
+ object directly for the obvious reason that you must start
280
+ from someonere.
281
+
282
+ ***** fx_app
283
+ To begin the declaration of your app, you must do the
284
+ following somewhere:
285
+ #+begin_src ruby
286
+ fx_app :app do
287
+ app_name "The Forbin Project"
288
+ vendor_name "Colossus"
289
+ ...
290
+ end
291
+ #+end_src
292
+
293
+ Typeically you'd do this inside of a module, but you could do it also
294
+ in a class body. Please see the examples.
295
+
296
+ ***** TODO instance
297
+ ***** TODO ingress_handler
298
+ ***** TODO deferred_setup
299
+ ***** TODO Mapping between fx_* declarations and the FX* FXRuby objects
300
+ ***** binding.fx
301
+ This is a way to split up your layouts into different .fx "modules", purely for
302
+ organizational reasons. For example,
303
+
304
+ #+begin_src ruby
305
+ binding.fx "overview"
306
+ #+end_src
307
+
308
+ will load the overview.fx portion of the GUI, which happens to be a tab contents
309
+ in the tab book, which in our case looks like:
310
+
311
+ #+begin_src ruby
312
+ # Overview Tab
313
+
314
+ fx_tab_item { text "&Overview" }
315
+ fx_horizontal_frame (:overview_info) {
316
+ opts STD_FRAME|LAYOUT_FILL_Y
317
+
318
+ fx_group_box (:ov_connections_group) {
319
+ text "Connections"
320
+ opts STD_GROUPBOX|LAYOUT_FILL_Y
321
+
322
+ fx_vertical_frame {
323
+ opts LAYOUT_FILL_Y|LAYOUT_FILL_X #|PACK_UNIFORM_HEIGHT
324
+
325
+ fx_group_box (:ov_conn_rabbitmq) {
326
+ ...
327
+ #+end_src
328
+
329
+ *** Examples
330
+ Class-based enhancement (this has not been tested yet!!!):
331
+ #+begin_src ruby
332
+ class Main < FXMainWindow
333
+ compose :my_window do
334
+ title "RubyNEAT Panel"
335
+ show PLACEMENT_SCREEN
336
+ width 700
337
+ height 400
338
+ fx_tab_book :my_book do |tab_book_ob|
339
+ x 0
340
+ y 0
341
+ width 500
342
+ height 100
343
+ pad_bottom 10
344
+ fx_text :my_text1, :my_window { |text_ob|
345
+ width 200
346
+ height 100
347
+ text_ob.target my_window: :on_click
348
+ }
349
+ fx_text :my_text2, :my_window { |text_ob|
350
+ width 200
351
+ height 100
352
+ text_ob { |t| puts "called after object initialization" }
353
+ }
354
+ end
355
+ end
356
+
357
+ def on_click
358
+ ...
359
+ end
360
+ end
361
+ #+end_src
362
+
363
+ Class-free enhancement:
364
+ #+begin_src ruby
365
+ mw = fx_main_window :my_window do
366
+ title "RubyNEAT Panel"
367
+ width 700
368
+ height 400
369
+ opts DECOR_ALL
370
+ x 10
371
+ y 10
372
+ instance { show PLACEMENT_SCREEN }
373
+ fx_tab_book :my_book do |tab_book_ob|
374
+ x 0
375
+ y 0
376
+ width 500
377
+ height 100
378
+ pad_bottom 10
379
+ fx_text :my_text1, :my_window { |text_ob|
380
+ width 200
381
+ height 100
382
+ instance my_window: :on_click
383
+ }
384
+ fx_text :my_text2, :my_window {
385
+ width 200
386
+ height 100
387
+ instance { |t| puts "called after object initialization" }
388
+ }
389
+ end
390
+ end
391
+
392
+ def mw.on_click
393
+ ...
394
+ end
395
+ end
396
+ #+end_src
397
+
398
+ **** [[file:examples/hello.rb][Hello World]] example (full) the Enhancement Way
399
+ #+begin_src ruby
400
+ #!/usr/bin/env ruby
401
+ require 'fxruby-enhancement'
402
+
403
+ include Fox
404
+ include Fox::Enhancement::Mapper
405
+
406
+ fx_app :app do
407
+ app_name "Hello"
408
+ vendor_name "Example"
409
+
410
+ fx_main_window(:main) {
411
+ title "Hello"
412
+ opts DECOR_ALL
413
+
414
+ fx_button {
415
+ text "&Hello, World"
416
+ selector FXApp::ID_QUIT
417
+
418
+ instance { |b|
419
+ b.target = ref(:app)
420
+ }
421
+ }
422
+
423
+ instance { |w|
424
+ w.show PLACEMENT_SCREEN
425
+ }
426
+ }
427
+ end
428
+
429
+ # alias for fox_component is fxc
430
+ fox_component :app do |app|
431
+ app.launch
432
+ end
433
+ #+end_src
434
+
435
+ **** Hello World the old fxruby way:
436
+ #+begin_src ruby
437
+ #!/usr/bin/env ruby
438
+
439
+ require 'fox16'
440
+
441
+ include Fox
442
+
443
+ application = FXApp.new("Hello", "FoxTest")
444
+ main = FXMainWindow.new(application, "Hello", nil, nil, DECOR_ALL)
445
+ FXButton.new(main, "&Hello, World!", nil, application, FXApp::ID_QUIT)
446
+ application.create()
447
+ main.show(PLACEMENT_SCREEN)
448
+ application.run()
449
+ #+end_src
450
+
451
+ Even though the old way has a slightly smaller line count, you can
452
+ see how messy it can be assigning each newly-created object to
453
+ a variable, and then having to pass that variable to the children.
454
+ Perhaps this example is too small, but perhaps the next one will
455
+ more illustrative.
456
+
457
+ **** [[file:examples/bounce.rb][Bouncing Ball]] example (full):
458
+ #+begin_src ruby
459
+ #!/usr/bin/env ruby
460
+ require 'fxruby-enhancement'
461
+
462
+ include Fox
463
+ include Fox::Enhancement::Mapper
464
+
465
+ ANIMATION_TIME = 20
466
+
467
+ class Ball
468
+ attr_reader :color
469
+ attr_reader :center
470
+ attr_reader :radius
471
+ attr_reader :dir
472
+ attr_reader :x, :y
473
+ attr_reader :w, :h
474
+ attr_accessor :worldWidth
475
+ attr_accessor :worldHeight
476
+
477
+
478
+ def initialize r
479
+ @radius = r
480
+ @w = 2*@radius
481
+ @h = 2*@radius
482
+ @center = FXPoint.new(50, 50)
483
+ @x = @center.x - @radius
484
+ @y = @center.y - @radius
485
+ @color = FXRGB(255, 0, 0) # red
486
+ @dir = FXPoint.new(-1, -1)
487
+ setWorldSize(1000, 1000)
488
+ end
489
+
490
+ # Draw the ball into this device context
491
+ def draw(dc)
492
+ dc.setForeground(color)
493
+ dc.fillArc(x, y, w, h, 0, 64*90)
494
+ dc.fillArc(x, y, w, h, 64*90, 64*180)
495
+ dc.fillArc(x, y, w, h, 64*180, 64*270)
496
+ dc.fillArc(x, y, w, h, 64*270, 64*360)
497
+ end
498
+
499
+ def bounce_x
500
+ @dir.x=-@dir.x
501
+ end
502
+
503
+ def bounce_y
504
+ @dir.y=-@dir.y
505
+ end
506
+
507
+ def collision_y?
508
+ (y<0 && dir.y<0) || (y+h>worldHeight && dir.y>0)
509
+ end
510
+
511
+ def collision_x?
512
+ (x<0 && dir.x<0) || (x+w>worldWidth && dir.x>0)
513
+ end
514
+
515
+ def setWorldSize(ww, wh)
516
+ @worldWidth = ww
517
+ @worldHeight = wh
518
+ end
519
+
520
+ def move(units)
521
+ dx = dir.x*units
522
+ dy = dir.y*units
523
+ center.x += dx
524
+ center.y += dy
525
+ @x += dx
526
+ @y += dy
527
+ if collision_x?
528
+ bounce_x
529
+ move(units)
530
+ end
531
+ if collision_y?
532
+ bounce_y
533
+ move(units)
534
+ end
535
+ end
536
+ end
537
+
538
+ fx_app :app do
539
+ app_name "Bounce"
540
+ vendor_name "Example"
541
+
542
+ fx_image(:back_buffer) { opts IMAGE_KEEP }
543
+
544
+ fx_main_window(:bounce_window) {
545
+ title "Bounce Demo"
546
+ opts DECOR_ALL
547
+ width 400
548
+ height 300
549
+
550
+ instance { |w|
551
+ def w.ball
552
+ @ball ||= Ball.new(20)
553
+ end
554
+
555
+ def w.drawScene(drawable)
556
+ FXDCWindow.new(drawable) { |dc|
557
+ dc.setForeground(FXRGB(255, 255, 255))
558
+ dc.fillRectangle(0, 0, drawable.width, drawable.height)
559
+ ball.draw(dc)
560
+ }
561
+ end
562
+
563
+ def w.updateCanvas
564
+ ball.move(10)
565
+ drawScene(ref(:back_buffer))
566
+ ref(:canvas).update
567
+ end
568
+
569
+ #
570
+ # Handle timeout events
571
+ #
572
+ def w.onTimeout(sender, sel, ptr)
573
+ # Move the ball and re-draw the scene
574
+ updateCanvas
575
+
576
+ # Re-register the timeout
577
+ ref(:app).addTimeout(ANIMATION_TIME, ref(:bounce_window).method(:onTimeout))
578
+
579
+ # Done
580
+ return 1
581
+ end
582
+
583
+ w.show PLACEMENT_SCREEN
584
+ ref(:app).addTimeout(ANIMATION_TIME, w.method(:onTimeout))
585
+ }
586
+
587
+ fx_canvas(:canvas) {
588
+ opts LAYOUT_FILL_X|LAYOUT_FILL_Y
589
+
590
+ instance { |c|
591
+ c.sel_paint { |sender, sel, event|
592
+ FXDCWindow.new(sender, event) { |dc|
593
+ dc.drawImage(ref(:back_buffer), 0, 0)
594
+ }
595
+ }
596
+
597
+ c.sel_configure{ |sender, sel, event|
598
+ bb = ref(:back_buffer)
599
+ bb.create unless bb.created?
600
+ bb.resize(sender.width, sender.height)
601
+ ref(:bounce_window) do |bw|
602
+ bw.ball.setWorldSize(sender.width, sender.height)
603
+ bw.drawScene(bb)
604
+ end
605
+ }
606
+ }
607
+ }
608
+ }
609
+ end
610
+
611
+ if __FILE__ == $0
612
+ # alias for fox_component is fxc
613
+ fox_component :app do |app|
614
+ app.launch
615
+ end
616
+ end
617
+ #+end_src
618
+
619
+ **** Bouncing Ball the old fxruby way:
620
+ #+begin_src ruby
621
+ require 'fox16'
622
+
623
+ include Fox
624
+
625
+ # How long to pause between updates (in milliseconds)
626
+ ANIMATION_TIME = 20
627
+
628
+ class Ball
629
+
630
+ attr_reader :color
631
+ attr_reader :center
632
+ attr_reader :radius
633
+ attr_reader :dir
634
+ attr_reader :x, :y
635
+ attr_reader :w, :h
636
+ attr_accessor :worldWidth
637
+ attr_accessor :worldHeight
638
+
639
+ # Returns an initialized ball
640
+ def initialize(r)
641
+ @radius = r
642
+ @w = 2*@radius
643
+ @h = 2*@radius
644
+ @center = FXPoint.new(50, 50)
645
+ @x = @center.x - @radius
646
+ @y = @center.y - @radius
647
+ @color = FXRGB(255, 0, 0) # red
648
+ @dir = FXPoint.new(-1, -1)
649
+ setWorldSize(1000, 1000)
650
+ end
651
+
652
+ # Draw the ball into this device context
653
+ def draw(dc)
654
+ dc.setForeground(color)
655
+ dc.fillArc(x, y, w, h, 0, 64*90)
656
+ dc.fillArc(x, y, w, h, 64*90, 64*180)
657
+ dc.fillArc(x, y, w, h, 64*180, 64*270)
658
+ dc.fillArc(x, y, w, h, 64*270, 64*360)
659
+ end
660
+
661
+ def bounce_x
662
+ @dir.x=-@dir.x
663
+ end
664
+
665
+ def bounce_y
666
+ @dir.y=-@dir.y
667
+ end
668
+
669
+ def collision_y?
670
+ (y<0 && dir.y<0) || (y+h>worldHeight && dir.y>0)
671
+ end
672
+
673
+ def collision_x?
674
+ (x<0 && dir.x<0) || (x+w>worldWidth && dir.x>0)
675
+ end
676
+
677
+ def setWorldSize(ww, wh)
678
+ @worldWidth = ww
679
+ @worldHeight = wh
680
+ end
681
+
682
+ def move(units)
683
+ dx = dir.x*units
684
+ dy = dir.y*units
685
+ center.x += dx
686
+ center.y += dy
687
+ @x += dx
688
+ @y += dy
689
+ if collision_x?
690
+ bounce_x
691
+ move(units)
692
+ end
693
+ if collision_y?
694
+ bounce_y
695
+ move(units)
696
+ end
697
+ end
698
+ end
699
+
700
+ class BounceWindow < FXMainWindow
701
+
702
+ include Responder
703
+
704
+ def initialize(app)
705
+ # Initialize base class first
706
+ super(app, "Bounce", :opts => DECOR_ALL, :width => 400, :height => 300)
707
+
708
+ # Set up the canvas
709
+ @canvas = FXCanvas.new(self, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y)
710
+
711
+ # Set up the back buffer
712
+ @backBuffer = FXImage.new(app, nil, IMAGE_KEEP)
713
+
714
+ # Handle expose events (by blitting the image to the canvas)
715
+ @canvas.connect(SEL_PAINT) { |sender, sel, evt|
716
+ FXDCWindow.new(sender, evt) { |dc|
717
+ dc.drawImage(@backBuffer, 0, 0)
718
+ }
719
+ }
720
+
721
+ # Handle resize events
722
+ @canvas.connect(SEL_CONFIGURE) { |sender, sel, evt|
723
+ @backBuffer.create unless @backBuffer.created?
724
+ @backBuffer.resize(sender.width, sender.height)
725
+ @ball.setWorldSize(sender.width, sender.height)
726
+ drawScene(@backBuffer)
727
+ }
728
+
729
+ @ball = Ball.new(20)
730
+ end
731
+
732
+ #
733
+ # Draws the scene into the back buffer
734
+ #
735
+ def drawScene(drawable)
736
+ FXDCWindow.new(drawable) { |dc|
737
+ dc.setForeground(FXRGB(255, 255, 255))
738
+ dc.fillRectangle(0, 0, drawable.width, drawable.height)
739
+ @ball.draw(dc)
740
+ }
741
+ end
742
+
743
+ def updateCanvas
744
+ @ball.move(10)
745
+ drawScene(@backBuffer)
746
+ @canvas.update
747
+ end
748
+
749
+ #
750
+ # Handle timeout events
751
+ #
752
+ def onTimeout(sender, sel, ptr)
753
+ # Move the ball and re-draw the scene
754
+ updateCanvas
755
+
756
+ # Re-register the timeout
757
+ getApp().addTimeout(ANIMATION_TIME, method(:onTimeout))
758
+
759
+ # Done
760
+ return 1
761
+ end
762
+
763
+ #
764
+ # Create server-side resources
765
+ #
766
+ def create
767
+ # Create base class
768
+ super
769
+
770
+ # Create the image used as the back-buffer
771
+ @backBuffer.create
772
+
773
+ # Draw the initial scene into the back-buffer
774
+ drawScene(@backBuffer)
775
+
776
+ # Register the timer used for animation
777
+ getApp().addTimeout(ANIMATION_TIME, method(:onTimeout))
778
+
779
+ # Show the main window
780
+ show(PLACEMENT_SCREEN)
781
+ end
782
+ end
783
+
784
+ if __FILE__ == $0
785
+ FXApp.new("Bounce", "FXRuby") do |theApp|
786
+ BounceWindow.new(theApp)
787
+ theApp.create
788
+ theApp.run
789
+ end
790
+ end
791
+ #+end_src
792
+
793
+ The Ball class is the same, but the actual Fox-related code
794
+ should clearly illustrate the power of Enhancement.
795
+
796
+ More examples can be found [[file:examples][HERE]].
797
+
798
+ ** Release Notes
799
+ | Version | Date | Notes |
800
+ |---------+------------+-----------------|
801
+ | 0.0.2 | 2017-01-11 | Initial release |
802
+
803
+ ** Known Issues
804
+ | Version | Date | Issues |
805
+ |---------+------------+----------------------------|
806
+ | 0.0.2 | 2017-01-11 | Not enough example code!!! |
807
+
808
+ ** Contributing to fxruby-enhancement
809
+
810
+ - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
811
+ - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
812
+ - Fork the project.
813
+ - Start a feature/bugfix branch.
814
+ - Commit and push until you are happy with your contribution.
815
+ - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
816
+ - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
817
+
818
+ ** Copyright and Licensing
819
+ Copyright (c) 2016-2017 Fred Mitchell. See [[file:LICENSE.txt][MIT License]] for
820
+ further details.
821
+ ** The Junkyard / Scratchpad
822
+ These are my personal notes, not meant for anyone else.
823
+ You may see some interesting tidbits here, but I am not
824
+ gauranteeing anything to be useful or reliable in this
825
+ section. YOU HAVE BEEN WARNED.
826
+ *** Genesis of the meta-meta programming, whereby brain goes boom
827
+ #+begin_src ruby
828
+ class FXToolBar # monkey patch
829
+ include Enhancement
830
+ attr_accessor :_o
831
+ end
832
+
833
+ def fx_tool_bar name, &block # DSL
834
+ o = OStruct.new
835
+ o.title = "default title"
836
+ ...
837
+
838
+ def o.title t
839
+ @title = t
840
+ end
841
+
842
+ def o.instance a, &block
843
+ o.instance_time_block = block
844
+ end
845
+ f = FXToolBar.new ...
846
+ f._o = o
847
+ end
848
+
849
+ <% for @class, @details in @api %>
850
+ #<%= @class %> < <%= @details[:class][1] %>
851
+ <% unless @details[:initialize].nil? %>
852
+ <% for @iniparams in @details[:initialize] %>
853
+ #<%= @iniparams %>
854
+ <% end %>
855
+ <% else %>
856
+ #No initializer
857
+ <% end %>
858
+ <% end %>
859
+ #+end_src
860
+
861
+