libui_paradise 0.2.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2072 -0
  3. data/doc/README.gen +1931 -0
  4. data/doc/SNIPPETS.md +94 -0
  5. data/doc/todo/todo.md +16 -0
  6. data/lib/libui_paradise/autoinclude.rb +9 -0
  7. data/lib/libui_paradise/base/base.rb +154 -0
  8. data/lib/libui_paradise/colours/colours.rb +14 -0
  9. data/lib/libui_paradise/domain_specific_language/README.md +5 -0
  10. data/lib/libui_paradise/domain_specific_language/button.yml +7 -0
  11. data/lib/libui_paradise/examples/complex/001_hbox_example.rb +17 -0
  12. data/lib/libui_paradise/examples/complex/002_tabs_example.rb +25 -0
  13. data/lib/libui_paradise/examples/complex/003_open_file_button_example.rb +38 -0
  14. data/lib/libui_paradise/examples/complex/004_font_button.rb +41 -0
  15. data/lib/libui_paradise/examples/complex/005_search_entry_example.rb +18 -0
  16. data/lib/libui_paradise/examples/complex/006_coloured_boxes_example.rb +22 -0
  17. data/lib/libui_paradise/examples/complex/007_slider_example.rb +31 -0
  18. data/lib/libui_paradise/examples/complex/008_radio_button_example.rb +23 -0
  19. data/lib/libui_paradise/examples/complex/009_separator_example.rb +25 -0
  20. data/lib/libui_paradise/examples/complex/010_table_example.rb +55 -0
  21. data/lib/libui_paradise/examples/complex/011_two_buttons_showing_how_to_enable_and_disable_them.rb +32 -0
  22. data/lib/libui_paradise/examples/complex/012_password_entry_example.rb +29 -0
  23. data/lib/libui_paradise/examples/complex/013_form_example.rb +38 -0
  24. data/lib/libui_paradise/examples/complex/014_text_example.rb +18 -0
  25. data/lib/libui_paradise/examples/complex/015_text_view_example.rb +16 -0
  26. data/lib/libui_paradise/examples/complex/016_grid_example.rb +31 -0
  27. data/lib/libui_paradise/examples/complex/017_unicode_text_example.rb +30 -0
  28. data/lib/libui_paradise/examples/complex/018_spinbutton_example.rb +33 -0
  29. data/lib/libui_paradise/examples/complex/019_combo_box_example.rb +50 -0
  30. data/lib/libui_paradise/examples/complex/020_checkbox_example.rb +81 -0
  31. data/lib/libui_paradise/examples/complex/021_font_example.rb +115 -0
  32. data/lib/libui_paradise/examples/complex/022_simple_notepad_example.rb +25 -0
  33. data/lib/libui_paradise/examples/complex/023_msg_box_error.rb +27 -0
  34. data/lib/libui_paradise/examples/complex/024_parse_config_file_example.config +6 -0
  35. data/lib/libui_paradise/examples/complex/024_parse_config_file_example.rb +15 -0
  36. data/lib/libui_paradise/examples/complex/025_colour_button.rb +53 -0
  37. data/lib/libui_paradise/examples/complex/026_basic_table_image.rb +96 -0
  38. data/lib/libui_paradise/examples/complex/027_basic_button_example.rb +19 -0
  39. data/lib/libui_paradise/examples/complex/028_try_for_automatic_button_press_event_after_a_delay.rb +47 -0
  40. data/lib/libui_paradise/examples/complex/029_progressbar_example.rb +116 -0
  41. data/lib/libui_paradise/examples/complex/030_entry_responds_to_comment_as_synonymous_to_the_enter_key_pressed_example.rb +140 -0
  42. data/lib/libui_paradise/examples/complex/031_notification_functionality_example.rb +96 -0
  43. data/lib/libui_paradise/examples/complex/032_simple_window_example.rb +69 -0
  44. data/lib/libui_paradise/examples/complex/033_daemonize_and_exit_after_delay_example.rb +91 -0
  45. data/lib/libui_paradise/examples/simple/001_open_file_example.rb +35 -0
  46. data/lib/libui_paradise/examples/simple/002_histogram_example.rb +213 -0
  47. data/lib/libui_paradise/examples/simple/003_fancy_text_example.rb +220 -0
  48. data/lib/libui_paradise/examples/simple/004_date_time_picker.rb +52 -0
  49. data/lib/libui_paradise/examples/simple/005_text_drawing_example.rb +185 -0
  50. data/lib/libui_paradise/examples/simple/006_midi_player.rb +96 -0
  51. data/lib/libui_paradise/examples/simple/007_control_gallery.rb +191 -0
  52. data/lib/libui_paradise/examples/simple/008_basic_area_example.rb +56 -0
  53. data/lib/libui_paradise/examples/simple/009_spectrum.rb +107 -0
  54. data/lib/libui_paradise/examples/simple/README.md +7 -0
  55. data/lib/libui_paradise/experimental/dsl.rb +17 -0
  56. data/lib/libui_paradise/extensions/counters.rb +58 -0
  57. data/lib/libui_paradise/extensions/extensions.rb +29 -0
  58. data/lib/libui_paradise/extensions/hash_fiddle_pointer_widgets.rb +150 -0
  59. data/lib/libui_paradise/extensions/misc.rb +754 -0
  60. data/lib/libui_paradise/fiddle/pointer.rb +1158 -0
  61. data/lib/libui_paradise/generic_window/README.md +9 -0
  62. data/lib/libui_paradise/generic_window/generic_window.rb +79 -0
  63. data/lib/libui_paradise/images/LIBUI_PARADISE_LOGO.png +0 -0
  64. data/lib/libui_paradise/images/README.md +2 -0
  65. data/lib/libui_paradise/images/form_example.png +0 -0
  66. data/lib/libui_paradise/libui_classes/box.rb +156 -0
  67. data/lib/libui_paradise/libui_classes/grid.rb +55 -0
  68. data/lib/libui_paradise/libui_classes/libui_classes.rb +1611 -0
  69. data/lib/libui_paradise/project/project.rb +28 -0
  70. data/lib/libui_paradise/prototype/README.md +3 -0
  71. data/lib/libui_paradise/prototype/prototype.rb +107 -0
  72. data/lib/libui_paradise/requires/require_the_libui_classes.rb +29 -0
  73. data/lib/libui_paradise/requires/require_the_libui_paradise_project.rb +12 -0
  74. data/lib/libui_paradise/toplevel_methods/misc.rb +13 -0
  75. data/lib/libui_paradise/version/version.rb +17 -0
  76. data/lib/libui_paradise.rb +1 -0
  77. data/libui_paradise.gemspec +49 -0
  78. data/test/testing_generic_window.rb +20 -0
  79. metadata +156 -0
data/doc/README.gen ADDED
@@ -0,0 +1,1931 @@
1
+ GOBOLINUX_IS_GREAT
2
+ ADD_LAST_UPDATE
3
+
4
+ ## The libui_paradise project
5
+
6
+ <img src="https://i.imgur.com/hYf3sum.png" style="margin: 0.75em; padding: 8px">
7
+
8
+ (This image has been partially auto-generated via **cfdg**, then modified
9
+ by me via **gimp** and ImageMagick - the rounded borders were
10
+ done via ImageMagick. You can re-use this image if you would like to,
11
+ including the colour-pattern, via a **CC BY 3.0** licence. See the following
12
+ link for that licence: https://creativecommons.org/licenses/by/3.0/. For
13
+ **cfdg** itself, have a look at: https://www.contextfreeart.org/gallery/)
14
+
15
+ The **libui_paradise project** aims to enhance the official (upstream)
16
+ ruby-libui bindings a little bit.
17
+
18
+ You can find the upstream ruby-libui bindings, maintained by **kojix2**,
19
+ here:
20
+
21
+ https://rubygems.org/gems/libui
22
+
23
+ (Or visit the github page for ruby-libui here via this link:
24
+ https://github.com/kojix2/LibUI )
25
+
26
+ (Be wary of the name that I use on this page - the currently maintained gem is
27
+ called **libui**; the older gem, which was called **libui-ruby**, is no longer
28
+ maintained since as of **2019**. In the document here I may call kojix2' gem
29
+ **ruby-libui**, but the official name is simply **libui**, which refers to the
30
+ gem mentioned above: https://rubygems.org/gems/libui - I understand that I
31
+ am using a misnomer when I refer to this as **ruby-libui**, but it helps
32
+ me remember, so ...)
33
+
34
+ Aside from this mentioned goal of **trying to enhance** the upstream ruby-bindings,
35
+ the **libui_paradise** project also attempts to demonstrate how we could **try to
36
+ use a DSL** to write less code in the long run; at the least less syntax.
37
+
38
+ Less syntax is not automatically a win-win situation, but often it can
39
+ be very useful.
40
+
41
+ Example for this:
42
+
43
+ _ = button('Hello world!') # for libui
44
+
45
+ You can use a similar variant in ruby-gtk3, for example:
46
+
47
+ _ = Gtk::Button.new('Hello world!')
48
+
49
+ Or, if you want to use "toplevel-methods", like the above, identical to the
50
+ first variant:
51
+
52
+ _ = button('Hello world!') # for ruby-gtk
53
+
54
+ In fact: if you notice the above three lines of code then there is indeed no
55
+ difference between which toolkit to use between the first variant and
56
+ the last variant. We could then, in theory, "plug in" different toolkits,
57
+ be it ruby-gtk, ruby-tk, ruby-libui and so forth. The **glimmer** project
58
+ is doing this to some extent, as a general DSL wrapper over GUI-related
59
+ functionality, including the www - see here: https://github.com/AndyObtiva/glimmer
60
+
61
+ We could even extend this to the www and generate the appropriate
62
+ tags that way, by treating HTML tags as "objects". Note that this is
63
+ "in theory"; in practice there are some shortcomings, or limitations,
64
+ such as different toolkits not supporting the same widgets or
65
+ functionality. The old ruby-qt toolkit, for example, required you to
66
+ connect slots to signals, due to qt requiring this. I do not know if
67
+ this is still the case or not, but back in the days this was
68
+ necessary and it complicated using ruby-qt a little bit.
69
+
70
+ The **libui_paradise gem** here is highly experimental at this stage
71
+ and may not work for all use cases, or may have bugs - I am still
72
+ learning myself here. I want to see which API calls make the most sense
73
+ in the long run, ideally even across different GUIs as well as the
74
+ www. (For those of you who have some experience with ruby-gtk this may
75
+ seem familiar, as pointed out above - API elements such as **.set_text()**
76
+ or **widget1.add(widget2)**; I kind of learned GUIs first via
77
+ **ruby-gtk**, which evidently shaped my opinion on GUI layouts
78
+ to some extent.)
79
+
80
+ The official ruby-libui project maintained by kojix2 comes with
81
+ **13 examples** (August 2021) so far. I assume that more examples
82
+ may be added over the coming months and years depending on use
83
+ case, time availability, motivation and so forth. Have a look at
84
+ the code provided on the github page to understand what is going
85
+ on - in particular the **histogram** example is really nifty if
86
+ you want to play around with it **interactively**. It is probably the
87
+ best example in this regard, because it features interactive and
88
+ dynamic use in a visual way - you can choose the colours, for
89
+ example. A spin-button allows for visual change too, but I think
90
+ colours are more impressive than simple up-and-down counters.
91
+
92
+ On windows this may look like so (via kotlin-libui):
93
+
94
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/histogram/histogram-windows7.png" style="margin-left: 2em">
95
+
96
+ Also check out kojix2' other examples in ruby-gtk - would be great
97
+ if we could have the same in libui one day, but this also depends
98
+ on what upstream libui makes available, unless it is somehow
99
+ possible to connect multiple shared libraries into libui; then
100
+ we could perhaps extend libui.
101
+
102
+ I am trying to think about ways to simplify the code in these
103
+ examples as well, so that we can "do more by writing less". But as
104
+ stated before: this is all **highly experimental** and subject
105
+ to change. I am going via slow, tiny babysteps here! Constant
106
+ wins the race in the long run, just remember the turtle versus
107
+ the rabbit. The rabbit always believed that he would win the
108
+ race ...
109
+
110
+ **kojix2** pointed out that a half-baked or incomplete OOP design may
111
+ not make a lot of sense in regards to ruby-libui - we have to think a
112
+ bit about **Fiddle::Pointer** which not everyone may have done before.
113
+
114
+ Otherwise you may end up segfaulting everything all over the place
115
+ (which I did run into already - but it is both scary and fun at the
116
+ same time!). Subclassing is also difficult to do properly - which
117
+ functions should be called to create a "proper" subclass? May there
118
+ be problems as a consequence of trying this? I have no idea right
119
+ now. This is definitely more related to the C API of ruby than
120
+ "pure", plain ruby as such. You kind of have to understand the
121
+ underlying C code to some extent.
122
+
123
+ I do not really know C very well; **pointers** are way above my
124
+ level of understanding, so **kojix2's** point is a fair one
125
+ to make, since someone may have to maintain a growing code base -
126
+ this may well be the case here too, so 'defensive programming'
127
+ is a viable strategy. There are examples where one has to work
128
+ around ruby's GC too, for instance - this all makes this a little
129
+ bit more complicated than plain, "pure" ruby code.
130
+
131
+ **However had**, at the same I still want to **experiment** and see
132
+ what may happen with the code in general - which pitfalls may happen
133
+ or which things could be improved upon. I think the more people use
134
+ libui and ruby-libui, the better this may become in the long run.
135
+ Imagine we could almost create a full desktop system based on
136
+ libui! Even if the original scope never was aimed towards that. :D
137
+
138
+ This may then be fast enough for ruby-on-the-desktop, and simple
139
+ enough to do that, too. Right now this has too many shortcomings;
140
+ I miss CSS styling in particular. But, in theory, we could do
141
+ this. Anyone feels clever enough to use ruby as a desktop-UI
142
+ language via libui? I suppose it still requires some decent
143
+ knowledge of C ...
144
+
145
+ Lots of different people could create widgets and add-ons when
146
+ this were possible ... but I digress.
147
+
148
+ **Write a GUI once, run everywhere** (well ... at the least in
149
+ theory). This is an excellent idea, even if libui may not be
150
+ around one day, that idea should be retained for other GUIs
151
+ in the future.
152
+
153
+ It's quite difficult to get GTK and ruby-gtk to work on
154
+ **windows** - I tried to compile it some weeks ago but I
155
+ ended up having "missing symbols" error messages afterwards.
156
+ I managed to get the hello-world.c example working, but the
157
+ more complicated examples did not work for me.
158
+
159
+ On Linux this is much, much easier to do ... I literally just
160
+ compile GTK, after its dependencies are properly installed (glib,
161
+ pango, atk, and so forth), and then the ruby bindings maintained
162
+ largely by kou (and others), and ... it works! At the least
163
+ on linux.
164
+
165
+ (I used to be able to run ruby-gtk2 on windows in the past,
166
+ a long time ago, using the provided binaries, but sadly upstream
167
+ GTK developers no longer provide binaries as-is, and there are
168
+ no binary bundles for ruby-gtk on windows anymore either. I
169
+ assume it is possible if you know msys2, and the windows
170
+ platform, but I am no expert on either, so ...)
171
+
172
+ **libui** is so much simpler to use on windows than GTK,
173
+ though - just do **gem install libui** and it'll work,
174
+ as-is. Literally. That's it. I tried it on my windows
175
+ laptop and it **does** indeed work. That convinced me
176
+ that it makes sense to use libui and ruby-libui. The reason it
177
+ works is because the ruby-libui gem (just called **libui**)
178
+ bundles the respective binaries, and it is quite small. This
179
+ would be much harder to do with ruby-gtk.
180
+
181
+ Now I am trying to find more awesome examples to showcase
182
+ what can be done. Who knows - perhaps even CSS may be supported
183
+ one day (perhaps only on linux though, which limits the
184
+ benefit of it; ruby-gtk3 does allow for CSS though). A
185
+ font example has also been provided, so we can upscale,
186
+ downscale, use different fonts, bold, in colours and so
187
+ on and so forth now.
188
+
189
+ Check out the colours in the file **basic_draw_text.rb**,
190
+ as courtesy provided by kojix2. That way you can style
191
+ the content of a widget more easily - only the fiddle-protection
192
+ against segfault parts is a bit strange, but that's a detail.
193
+
194
+ There currently is quite a bit of code to make this work;
195
+ in the long run I plan to simplify this if possible. **The
196
+ less code we have to write, the better** - as long as the
197
+ end code is still **readable**.
198
+
199
+ Note that the subclass situation may change eventually; see
200
+ upstream kojix2 and related discussion for fiddle. For
201
+ the time being, though, I'll retain my old approach until
202
+ I am sufficiently content with the project as-is. Right now
203
+ libui_paradise has way too much undocumented and untested
204
+ code and I still have not added all examples either (coloured
205
+ text, for instance, still has to be added; and more image-related
206
+ examples that are smaller). Stay tuned.
207
+
208
+ ## How to require the libui_paradise project
209
+
210
+ In order to **require the libui_paradise project**, use
211
+ something like the following:
212
+
213
+ require 'libui_paradise'
214
+
215
+ More frequently if you look at the **examples/** subdirectory, the
216
+ following is used instead:
217
+
218
+ require 'libui_paradise/autoinclude'
219
+
220
+ Note that this particular require call does a few things automatically,
221
+ which may not always be what you may want to use. So, again - check
222
+ out the official ruby-libui repository first, including the examples,
223
+ before having a look at **libui_paradise**. This project here is a
224
+ tinker-project, very unstable, subject to change - don't use it in
225
+ production yet.
226
+
227
+ In my own projects I tend to use the above autoinclude variant
228
+ most of the time, because that way I can write less code (omit
229
+ a few lines).
230
+
231
+ ## How to require libui itself
232
+
233
+ Simply do:
234
+
235
+ require 'libui'
236
+
237
+ Note that the **libui_paradise gem** does so automatically if you require it.
238
+ The idea here is to simply use **libui_paradise** directly, while still allowing
239
+ you to use **libui** at the same time as well, should you prefer to do so.
240
+
241
+ ## How to get started with ruby-libui and the libui_paradise project?
242
+
243
+ Well - as stated elsewhere, I first recommend to you that you look directly
244
+ at ruby-libui provided by kojix2, in particular the examples that he
245
+ distributes in the gem. Work through the examples step-by-step, possibly
246
+ starting with the smallest example, see whether they work (they do,
247
+ or should) and have a look at how things work - just to get an
248
+ overview.
249
+
250
+ Have a look at the code as well, in order to understand how it
251
+ works roughly; after some minutes or perhaps a very few hours of tinker-time
252
+ you should understand quite a bit already, if you already know ruby. Even
253
+ more so if you did work with GUIs before, including GUIs via a www-interface.
254
+
255
+ Then you are recommended to look at the libui_paradise project and look to
256
+ see what has been added on top of what kojix2 provides. Look at the examples
257
+ of the libui_paradise project as well (they are a bit simplified compared
258
+ to the upstream examples; unfortunately a few of them currently do not
259
+ work), then consider using **libui_paradise/prototype/prototype.rb** in
260
+ particular. Copy it and adjust it to your project and use case, as-is. You
261
+ may want to remove the grid that is inside there and use a hbox or a vbox
262
+ instead, or a padded_hbox and padded_vbox. It's all quite simple actually
263
+ once you understand the basic API: windows, widgets, buttons, entries.
264
+
265
+ For example, a button can be added in this way to a vbox, if you use
266
+ the libui_paradise gem:
267
+
268
+ outer_vbox = padded_vbox # padded means that it will have some padding to the sides
269
+ button = ui_button('Hello world!') # You can drop the ui_ prefix if you'd like to
270
+ button.on_clicked {
271
+ puts 'Hello world!'
272
+ }
273
+ outer_vbox.minimal(button) # Use minimal space if possible. Or use .add().
274
+
275
+ That's it! Inside of the **.on_clicked {}** block you can run the ruby code
276
+ that you want to use. A button that is in a container (such as **vbox**) that
277
+ outputs hello world when clicked. Can't get much simpler than that. \o/
278
+
279
+ You can omit the **ui_** part above. I just use it to point out the difference;
280
+ and because I'd otherwise may have to use "button = button" aka "button = button()"
281
+ which may be confusing- Thus, using **ui_button()** appeared to make more
282
+ sense in this regard.
283
+
284
+ ## How to add a margin in LibUI
285
+
286
+ Use something like:
287
+
288
+ LibUI.window_set_margined(MAIN_WINDOW, 1)
289
+
290
+ ## Fiddle::Pointer and playing with pointers
291
+
292
+ The **ruby-libui bindings** make use of **Fiddle::Pointer** a lot -
293
+ see the file called **ffi.rb** in the ruby-libui gem ("gem install libui").
294
+ It's like **magic** to me - scary and awesome at the same time.
295
+
296
+ As a consequence the libui_paradise project has to 'handle' pointers
297
+ as well, indirectly so, via whatever ruby-libui makes available.
298
+
299
+ I decided that, at the least for the time being, we will add
300
+ **ad-hoc code** straight to Fiddle::Pointer. This is not the
301
+ optimal solution and I do not recommend doing so, even more so as
302
+ we modify Fiddle::Pointer directly which I don't think is a good
303
+ idea; it may be better to have some proper data structures and
304
+ perhaps **subclass** from Fiddle::Pointer instead, and then modify
305
+ that subclass instead. That may be better. But for the time being,
306
+ the code here will remain as it is, until we can come up with
307
+ better ways to deal with the situation. For now **simplicity**
308
+ rules.
309
+
310
+ Some of the examples used require assigment to local variables
311
+ to avoid the GC to kick in and cause the program to end. This
312
+ is presently not very elegant - see a discussion between kojix2
313
+ and kou to improve on this part. For the time being, though,
314
+ some of the examples require 'boilerplate' assignment to
315
+ variabless. Stay tuned for improvements in this regard; ideally
316
+ we can use libui without having to play with pointers ever.
317
+
318
+ ## Constraints of the libui_paradise gem
319
+
320
+ The libui_paradise gem comes with some **constraints**.
321
+
322
+ For example, many of the ad-hoc methods on **Fiddle::Pointer**
323
+ will only work after you called e. g. **ui_vbox** or a
324
+ similar constructor where we registered which widgets are
325
+ created (aka the **new_** methods that are available on
326
+ the **LibUI** 'namespace'). Only when this has happened
327
+ will that widget become registered in the main Hash.
328
+
329
+ That then means, logically, that if you use a method such as:
330
+
331
+ check_button = ui_check_button
332
+ check_button.is_active?
333
+
334
+ This will only work if that widget was created already prior
335
+ to that "method call".
336
+
337
+ What this means in practice is that it is best if you
338
+ **create all the skeleton** (the **basic UI elements**) **before**
339
+ calling any other code, including on-clicked or on-toggle
340
+ events. Otherwise you may find nil values and don't
341
+ know why that is the case so. If this seems too complicated
342
+ for you, don't worry - the examples distributed via the
343
+ libui_paradise gem should work fine (except two buggy ones), so
344
+ the point of this subsection here is to keep your attention to
345
+ the fact that, as of right now, if you use libui_paradise, the
346
+ **proper-order-of-elements is important**. Ideally create all
347
+ the widgets first, before you add additional functionality to them.
348
+
349
+ This is admittedly not a very elegant limitation right
350
+ now, and one day this restrictions may be removed or
351
+ lifted - but for now, it is a limitation that requires
352
+ a tiny bit of discipline to work around, for the time being.
353
+ So, again - it is best to split up the skeleton UI from
354
+ the function.
355
+
356
+ ## How to use an 'Open File' button
357
+
358
+ Most <b>graphical user interfaces</b> have an "open a local file"
359
+ functionality.
360
+
361
+ I came up with the following solution for now:
362
+
363
+ outer_vbox = padded_vbox
364
+ button_open_file = button('Open file')
365
+ button_open_file.on_clicked {
366
+ filename = ui_open_file(window).to_s # This is the part that will open a local file.
367
+ }
368
+ outer_vbox << button_open_file # Add the button to the outer-vbox.
369
+
370
+ Calling the **.to_s** method on the Fiddle::Pointer yields the actual
371
+ filename. You can then add more code to deal with this.
372
+
373
+ **window** above refers to the **main_window**. See the example
374
+ **019_open_file_button_example.rb** for how this actually works.
375
+
376
+ In the future this may be even further simplified a little, as
377
+ opening local files is a very common task in most GUIs. A
378
+ single method probably should suffice for using such a
379
+ specialized button.
380
+
381
+ ## How to properly quit from a ruby-libui widget
382
+
383
+ I found that:
384
+
385
+ LibUI.quit
386
+
387
+ Works best overall. And seems to suffice as well.
388
+
389
+ There are some other calls, such as destroy-control and similar
390
+ actions, which are probably clean-up related - but by and large
391
+ **LibUI.quit** seems to be the main part how to exit from a
392
+ libui-application.
393
+
394
+ I document this here in the event that I forget it in a few
395
+ months.
396
+
397
+ If you use the libui_paradise gem then you can use the
398
+ following method to use a quit button:
399
+
400
+ a_quit_button = ui_quit_button
401
+
402
+ If you want another textual description then you can do the
403
+ following:
404
+
405
+ a_quit_button = ui_quit_button(text: 'Open a local file') # That text description would be confusing though
406
+ a_quit_button = ui_quit_button(text: 'Exit the application') # Much better now! \o/
407
+
408
+ So, as a reminder:
409
+
410
+ main_window = LibUI.window('Notepad', 500, 300, 1)
411
+ LibUI.close_properly(main_window)
412
+ # Or simpler:
413
+ close_properly(main_window)
414
+
415
+ You can also directly use a toplevel API such as:
416
+
417
+ LibUI.window_on_closing(main_window) {
418
+ LibUI.exit_from(main_window)
419
+ }
420
+
421
+ ## How to instantiate libui:
422
+
423
+ UI = LibUI
424
+ init = UI.init
425
+
426
+ Or just:
427
+
428
+ LibUI.init
429
+
430
+ Since as of 2022 I prefer the longer variant, e. g. **LibUI**init. The
431
+ old UI constant ("alias") will be retained, but new code added to the
432
+ libui_paradise gem will not use **UI** - instead the slightly longer
433
+ **LibUI** is used.
434
+
435
+ ## Subclassing
436
+
437
+ Currently subclassing from LibUI elements does not work - I simply
438
+ have no idea how to "subclass" from a **Fiddle::Pointer**. Perhaps we
439
+ have to build up a data structure that behaves like **Fiddle::Pointer**
440
+ but also has methods that allow for a more direct 'OOP behaviour'. Has
441
+ anyone tried this yet? I am scared to try considering I already got
442
+ segfaults everywhere ...
443
+
444
+ Eventually I may figure this out - or someone else with more knowledge
445
+ will make this available. We can probably 'fake' subclassing to
446
+ a pointer somehow ... after all ruby internally has to figure it
447
+ out as well and probably did so already, on the C-level side via
448
+ various functions.
449
+
450
+ ## Using scrolling-areas in LibUI
451
+
452
+ The following API can be used to create a new scrolling area:
453
+
454
+ LibUI.new_scrolling_area
455
+
456
+ Scrolling areas have horziontal and vertical scrollbars. The
457
+ amount that can be scrolled is determined by the area's
458
+ size, which is decided by the programmer (both when creating
459
+ the Area and by a call to SetSize). Only a portion of the
460
+ Area is visible at any time; drawing and mouse events are
461
+ automatically adjusted to match what portion is visible,
462
+ so you do not have to worry about scrolling in your
463
+ event handlers.
464
+
465
+ The method LibUI.new_scrolling_area() accepts three arguments.
466
+ The second and third are width and height, respectively
467
+ (as **integers**).
468
+
469
+ The first argument is the area handle. It has the following
470
+ pointer types (struct):
471
+
472
+ .Draw
473
+ .MouseEvent
474
+ .MouseCrossed
475
+ .DragBroken
476
+ .KeyEvent
477
+
478
+ The handlerDraw() function in C looks like this:
479
+
480
+ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p)
481
+ {
482
+ uiDrawTextLayout *textLayout;
483
+ uiFontDescriptor defaultFont;
484
+ uiDrawTextLayoutParams params;
485
+
486
+ params.String = attrstr;
487
+ uiFontButtonFont(fontButton, &defaultFont);
488
+ params.DefaultFont = &defaultFont;
489
+ params.Width = p->AreaWidth;
490
+ params.Align = (uiDrawTextAlign) uiComboboxSelected(alignment);
491
+ textLayout = uiDrawNewTextLayout(&params);
492
+ uiDrawText(p->Context, textLayout, 0, 0);
493
+ uiDrawFreeTextLayout(textLayout);
494
+ uiFreeFontButtonFont(&defaultFont);
495
+ }
496
+
497
+ The scrollable area may look like this:
498
+
499
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/hello/hello-windows.png">
500
+
501
+ ## Quickly adding text to a widget
502
+
503
+ You can use something like this if you use the libui_paradise gem:
504
+
505
+ outer_vbox = ui_vbox
506
+ outer_vbox.text(
507
+ 'This widget can be used to do xyz.'
508
+ )
509
+
510
+ Here .text() just means to add a text onto the vertical box.
511
+
512
+ Again - this modifies **Fiddle::Pointer**, so be wary when using
513
+ it. The second argument is the default one for use in a
514
+ label / ui_text widget.
515
+
516
+ ## Working with combo-boxes
517
+
518
+ To create a combo-box in vanilla libui, do this:
519
+
520
+ alignment = LibUI.new_combobox
521
+ LibUI.combobox_append(alignment, 'Left')
522
+ LibUI.combobox_append(alignment, 'Center')
523
+ LibUI.combobox_append(alignment, 'Right')
524
+ LibUI.combobox_set_selected(alignment, 0)
525
+ LibUI.combobox_on_selected(alignment) { on_combobox_selected(@area) }
526
+
527
+ This is a bit cumbersome, so if you use libui_paradise this
528
+ can be simplified a bit.
529
+
530
+ To create a combo-box do:
531
+
532
+ combo_box = ui_combo_box
533
+
534
+ You can fill it with an array of entries **on creation-time** via:
535
+
536
+ combo_box = ui_combo_box([1,2,3,4]) # simply pass your Array here
537
+ combo_box = ui_combobox(%w( Left Center Right )) # three members of this Array
538
+
539
+ This will also focus (aka **select**) on the very first element
540
+ when doing so by default, so you can get rid of 4-5 lines of
541
+ code. How fancy! \o/
542
+
543
+ If you need to do so manually, and focus on another element,
544
+ for example, then you can use the following toplevel method:
545
+
546
+ LibUI.combobox_set_selected()
547
+
548
+ For instance:
549
+
550
+ LibUI.combobox_set_selected(combobox, 0) # The first one will be active too.
551
+
552
+ To **query the currently selected value**, use:
553
+
554
+ LibUI.combobox_selected(pointer)
555
+
556
+ This is usually done via a **proc {}** object. See kojix2' examples.
557
+
558
+ In LibuiParadise a few custom methods were added, such as
559
+ **.ui_sync_connect()**. This method was added to connect a
560
+ combo-box to a entry and automatically sync that entry whenever
561
+ the combo box is changed. See the example **007_combo_box_example.rb**
562
+ for how this works.
563
+
564
+ Or, just as a one-liner example:
565
+
566
+ ui_sync_connect(@combo_box, @entry) # widget1, then widget2
567
+
568
+ This may not always work as you'd expect, so a third argument can
569
+ be passed (the array that populates the combo-box).
570
+
571
+ So:
572
+
573
+ ui_sync_connect(@combo_box, @entry, @array_here)
574
+
575
+ There are probably more elegant ways to solve this, but I only
576
+ wanted to solve this quickly and move on.
577
+
578
+ The **source code** to the combo-box in libui, at the least
579
+ for UNIX/Linux, can be seen here:
580
+
581
+ https://github.com/andlabs/libui/blob/master/unix/combobox.c
582
+
583
+ ## Error messages and ui_error_message
584
+
585
+ In LibUI respectively ruby-libui you can display error messages
586
+ via a popup window. These are also called **message-box** and
587
+ **error message box**, respectively.
588
+
589
+ The API is something like this:
590
+
591
+ LibUI.msg_box(main_window, 'Information', 'You clicked the button')
592
+ LibUI.msg_box_error(main_window, 'Information', 'You clicked the button')
593
+
594
+ The first line shows a normal message box; the second line shows how to
595
+ use a message box specifically 'adapted' for displaying errors to the
596
+ end user.
597
+
598
+ LibuiParadise makes this available via **ui_msg_box** and **ui_msg_box_error**
599
+ respectively.
600
+
601
+ ## Libui Form
602
+
603
+ **Form** is a container that takes labels for its contents. This is currently
604
+ just a stub though - we may have to research this with better examples.
605
+
606
+ ## Libui Checkbox
607
+
608
+ A simple checkbox example in **plain** ruby-libui follows:
609
+
610
+ checkbox = UI.checkbox('Checkbox')
611
+ checkbox_toggle_callback = proc { |ptr|
612
+ checked = UI.checkbox_checked(ptr) == 1
613
+ UI.checkbox_set_text(ptr, "I am the checkbox (#{checked})")
614
+ }
615
+
616
+ This may look like so on Linux:
617
+
618
+ <img src="https://i.imgur.com/d7qWalZ.png" style="margin-left: 2em; padding: 4px; border: 1px solid black;">
619
+
620
+ To query whether a checkbox is **active**, use code such as the
621
+ following:
622
+
623
+ checkbox.is_active?
624
+ checkbox.active?
625
+
626
+ This depends on the modifications to Fiddler::Pointer, so
627
+ be wary when you use this - there be dragons (perhaps). Most
628
+ of these modifications are based on **.object_id**, which is
629
+ registered in a main, toplevel Hash in the
630
+ **libui_paradise project**. Not very elegant, but simple, and
631
+ it works (for the most part).
632
+
633
+ ## Adding a widget into another widget
634
+
635
+ I chose the following **API** for this:
636
+
637
+ box1.add(box2, 1)
638
+
639
+ Note that this is "cheating" a bit because the method **.add()** is defined
640
+ on **Fiddle::Pointer**. That's scary! Segfaults coming your way. But it
641
+ also seems to work to some extent. Which is amazing ... :-)
642
+
643
+ In ruby-gtk it is quite common to use **.add()**. While **.pack_start()**
644
+ and **.pack_end()** are available in ruby-gtk as well, I think .add() is
645
+ the simpler name. We just **add a widget to another widget** - job done.
646
+
647
+ (I may also use << as alias to .add() and while << is great, remember
648
+ that it can not easily be used all the time, e. g. box1 << box2 <<
649
+ box3 versus box1.add(box2).add(box3) - the latter is a bit more
650
+ resilient syntax-wise.)
651
+
652
+ As stated, **<<** was added as an alias to **.add()** but I am not yet sure
653
+ if this is a very good idea. It's super-nifty to use << everywhere, but it
654
+ also changes the syntax of the whole .rb file ... on the other hand, using
655
+ << is easier than .add() so ... I'll go with that as well. Remember
656
+ there is more than one way to do something in ruby - you need to
657
+ select the variant(s) that work best for you and possibly ignore the
658
+ other variants.
659
+
660
+ Since a while the above can be simplified a bit, as will be shown
661
+ next.
662
+
663
+ Rather than use:
664
+
665
+ box1.add(box2, 1)
666
+
667
+ You can now do this instead:
668
+
669
+ box1.maximal(box2)
670
+
671
+ This is a tiny bit longer, but you can omit the ", 1" part, which is
672
+ nice. The alternative is .minimal(), which defaults to:
673
+
674
+ add(second_widget, 0)
675
+
676
+ So the only difference between .maximal() and .minimal() will be
677
+ whether you pass 0 or 1 to the method. See the upstream libui
678
+ API to understand the difference.
679
+
680
+ ## Libui Tabs
681
+
682
+ The notebook-tab may look like this:
683
+
684
+ <img src="https://i.imgur.com/olWQAIJ.png" style="margin-left: 2em">
685
+
686
+ ## Create a vertical box:
687
+
688
+ Use code like this:
689
+
690
+ vbox = UI.new_vertical_box
691
+
692
+ If you use the libui_paradise gem, you can use this:
693
+
694
+ vbox = ui_vbox # or
695
+ vbox = padded_vbox
696
+
697
+ ## Adding a horizontal separator or a vertical separator
698
+
699
+ The method **UI.new_horizontal_separator** can be used to add (or rather
700
+ first create) a horizontal separator.
701
+
702
+ You can then add it via .add() or << if you use the libui_paradise
703
+ project. Alternatively you can use the toplevel method provided by
704
+ ruby-libui, since that is what the libui_paradise project is doing anyway.
705
+
706
+ To simplify this further, you can do something like this:
707
+
708
+ outer_vbox = ui_vbox # I call the most-outer vbox usually outer-vbox
709
+ outer_vbox.add_hsep
710
+ # outer_vbox.add_horizontal_separator # Or this variant if you prefer some verbosity instead.
711
+
712
+ Or, perhaps better, use a padded vbox:
713
+
714
+ outer_vbox = padded_vbox # note that "ui_" is not used here
715
+ outer_vbox.add_hsep
716
+
717
+ I needed this functionality to quickly add horizontal separators for some
718
+ visual cue in the **User Interface**. Using **.add_hsep** is very
719
+ convenient and fast to write/type. If you want more verbosity then remember
720
+ that you can always use the upstream API as-is.
721
+
722
+ The same applies to vertical separators, but I haven't used them yet
723
+ actually. They do work, though.
724
+
725
+ For an interesting example, have a look at the following screenshot
726
+ from example **031_separator_example.rb**.
727
+
728
+ <img src="https://i.imgur.com/cXO6RF4.png" style="margin: 1em; margin-left: 3em">
729
+
730
+ What is interesting is that all the horizontal lines come from using
731
+ a hsep (horizontal separator).
732
+
733
+ The syntax is different though:
734
+
735
+ vbox.minimal(hsep, 1)
736
+ vbox.minimal(hsep, 0)
737
+
738
+ Carefully look at the area that surrounds the button in the middle.
739
+
740
+ The nearby area is padded, thanks to **vbox.minimal(hsep, 0)**. So
741
+ passing **0** means this is quite elegant - you add padding between
742
+ different elements. But when you pass 1 then you get this strange
743
+ big block. I am not sure if this is how it should be, but if it is
744
+ then you can sort of use that as an additional visual cue. I am
745
+ not necessarily recommending this be done, but **if** you ever have
746
+ such a use case then you can use it - which is another reason why
747
+ I added this screenshot, so that I don't forget. :)
748
+
749
+ ## Padding elements in LibUI
750
+
751
+ The general API for setting padding to a container in LibUI
752
+ goes via:
753
+
754
+ LibUI.box_set_padded(box, 1) # The value is usually either 0 or 1.
755
+
756
+ The alignment value may be as follows:
757
+
758
+ 0: Fill
759
+ 1: Start
760
+ 2: Center
761
+ 3: End
762
+
763
+ Positioning values may typically be:
764
+
765
+ 0: Left
766
+ 1: Top
767
+ 2: Right
768
+
769
+ Have a look at the example file <b>libui_paradise/examples/simple/007_control_gallery.rb</b>
770
+ to see how this could be used.
771
+
772
+ ## LibUI::FFI::DrawTextLayoutParams.malloc
773
+
774
+ LibUI::FFI::DrawTextLayoutParams.malloc can be used to draw
775
+ coloured text.
776
+
777
+ params = UI::FFI::DrawTextLayoutParams.malloc
778
+ params.to_ptr.free = Fiddle::RUBY_FREE
779
+
780
+ params.Align = 'Left' # Use left alignment here.
781
+
782
+ ## Margins in LibUI
783
+
784
+ The **margin** property specifies if the window content should have a
785
+ margin or not. The default value is false, meaning that there will
786
+ be no margin.
787
+
788
+ In order to understand the difference, a visual image may be
789
+ best - the first image that is shown next shows **no margin**,
790
+ whereas the second image **shows** a margin.
791
+
792
+ <img src="https://cloud.githubusercontent.com/assets/11197111/16465935/804a30d4-3e41-11e6-8189-150e8cddc152.png" style="margin-left: 2em"><br>
793
+ <img src="https://cloud.githubusercontent.com/assets/11197111/16465906/68304cfe-3e41-11e6-8ae0-3123205ee136.png" style="margin-left: 2em"><br>
794
+
795
+ Note that the API name is **margined** rather than **margin** -
796
+ got me a little while to get used to that name.
797
+
798
+ The API in Go is: __func (*Group) SetMargined__ respectively
799
+ __func (g *Group) SetMargined(margined bool)__. When **true**
800
+ then the group has margins around the child widgets, as mentioned
801
+ already.
802
+
803
+ The **size** of the margins in use, is, unfortunately, determined
804
+ automatically by the OS - we currently (2021) do not have control
805
+ over the size of the margin at hand via **libui**.
806
+
807
+ Code examples for 'raw' **ruby-libui** are these:
808
+
809
+ UI.window_set_margined(MAIN_WINDOW, 1)
810
+ UI.group_set_margined(group, 1)
811
+
812
+ Because I prefer calling methods on objects, I added this instead:
813
+
814
+ MAIN_WINDOW.is_margined
815
+ MAIN_WINDOW.uses_a_margin # Or this variant.
816
+
817
+ Note that we are here faking method-calls on a Fiddle::Pointer, but
818
+ if we don't pay attention then it looks like **OOP**! If it walks
819
+ like a duck, quacks like a duck then ... it may be a
820
+ **Fiddle::Pointer:Duck**!!!
821
+
822
+ You can use a toplevel margined window like this:
823
+
824
+ window = margined_window(:filename, 250, 150, 0)
825
+
826
+ :filename (as Symbol) is an 'alias' - if given then
827
+ the filename will become the title of the window at
828
+ hand. Only the raw filename will be used, so if you
829
+ have a file at **/tmp/foo/bar.rb** then the title
830
+ of the window will be **bar.rb**.
831
+
832
+ ## Entry
833
+
834
+ An entry in libui may look like this:
835
+
836
+ <img src="https://raw.githubusercontent.com/parro-it/libui-node/master/docs/media/UiEntry.png" style="margin-left:1em">
837
+
838
+ Such an entry can be set to be **read only** (readOnly: Boolean, aka true or false).
839
+
840
+ The upstream C code for libui-entry, for **unix/**, can be seen here:
841
+
842
+ https://github.com/andlabs/libui/blob/master/unix/entry.c
843
+
844
+ ## Borderless windows and fullscreen windows
845
+
846
+ A window that is **borderless: true** will not show any title or
847
+ outside frame. This may be useful for games and what not.
848
+
849
+ To set the main window to full screen (occupy the whole monitor) do:
850
+
851
+ LibUI.window_set_fullscreen(main_window, 1)
852
+
853
+ ## Spinbutton / Spinbox
854
+
855
+ You can use the following API for a spinbox:
856
+
857
+ UI.new_spinbox
858
+
859
+ To create a new spinbox.
860
+
861
+ To specify the **min** and **max** range, pass them as parameters
862
+ on creation-time:
863
+
864
+ UI.new_spinbox(0, 100)
865
+
866
+ If you use the extensions found in the libui_paradise gem then
867
+ you can do this instead:
868
+
869
+ ui_spinbox
870
+ spinbox # this is the simplest variant
871
+ spinbox(0, 100)
872
+
873
+ You can also be more explicit and use a hash such as the following
874
+ example shows:
875
+
876
+ spinbutton = ui_spinbutton(start: 0, end: 100) # Start at 0, end at 100.
877
+
878
+ You should be able to set a new value of the spinbox via **.set_value**. Have
879
+ a look at the file at **libui_paradise/examples/rb 021_spinbutton_example.rb**
880
+ to see how this works.
881
+
882
+ Relevant methods in regard to the spinbox in libui are as follows:
883
+
884
+ UI.spinbox_on_changed()
885
+ UI.spinbox_set_value()
886
+ UI.spinbox_value()
887
+
888
+ To **set** a value use either of the following two methods:
889
+
890
+ spinbox.set_value(42)
891
+ spinbox.value = 42 # this works as well
892
+
893
+ Here is a small image of how this looks:
894
+
895
+ <img src="https://i.imgur.com/FxNTRse.png" style="margin: 0.5em; margin-left: 2em">
896
+
897
+ ## Create a text-view widget
898
+
899
+ A text-view widget shows content, such as the content of a local file.
900
+
901
+ In libui the general API for this is:
902
+
903
+ UI.new_multiline_entry # this is a textview
904
+
905
+ ## Control Gallery
906
+
907
+ Here is an image, from kotlin-libui, how this may look on windows:
908
+
909
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/controlgallery/controlgallery-windows7.png" style="margin-left: 2em">
910
+
911
+ ## LibUI.new_button() - how to work with buttons in LibUI
912
+
913
+ <b>LibUI.new_button</b> allows us to create a new button.
914
+
915
+ Examples:
916
+
917
+ button1 = LibUI.new_button('Text')
918
+ button2 = LibUI.new_button('▶')
919
+
920
+ Now, we need to "tell" this button what to do when it is
921
+ clicked. This is done via Libui.button_on_clicked().
922
+
923
+ Example:
924
+
925
+ LibUI.button_on_clicked(button) do
926
+ LibUI.msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
927
+ end
928
+
929
+ ## Enabling / Disabling buttons in libui
930
+
931
+ This is, assumingly, already possible via:
932
+
933
+ libui/ui.h
934
+
935
+ Lines 105 to 107 in a0a9807
936
+ _UI_EXTERN int uiControlEnabled(uiControl *);
937
+ _UI_EXTERN void uiControlEnable(uiControl *);
938
+ _UI_EXTERN void uiControlDisable(uiControl *);
939
+
940
+ In ruby-libui this should be possible via:
941
+
942
+ LibUI.control_enable()
943
+ UI.control_enable()
944
+
945
+ LibUI.control_disable()
946
+ UI.control_disable()
947
+
948
+ See the example
949
+ **libui_paradise/examples/complex/011_two_buttons_showing_how_to_enable_and_disable_them.rb**
950
+ in how this works.
951
+
952
+ ## libui and ruby-libui internals
953
+
954
+ This is an incomplete subsection. I know almost nothing at all about
955
+ C; kojix2 knows more, so I refer you to the homepage of ruby-libui
956
+ respectively.
957
+
958
+ Most of the code for ruby-libui resides under **ffi.rb**. In August 2021
959
+ this file contains almost 1000 lines, including newlines. Still quite
960
+ some code. If you want to look at the raw content, have a look at
961
+ the following link for **ffi.rb**:
962
+
963
+ https://raw.githubusercontent.com/kojix2/LibUI/main/lib/libui/ffi.rb
964
+
965
+ The two most important components there, as far as I understand it,
966
+ are **try_extern** and **structs**.
967
+
968
+ For instance:
969
+
970
+ try_extern 'void uiOnShouldQuit(int (*f)(void *data), void *data)'
971
+
972
+ Control = struct [
973
+
974
+ I assume that any support made available for ruby **must** have a
975
+ corresponding entry in **ffi.rb**. At the least this is how I
976
+ understood it.
977
+
978
+ If it is not in **ffi.rb** then support for that was not (yet) added.
979
+
980
+ Individual enum entries can also be seen. For instance, for font-related
981
+ data the following attributes are in use:
982
+
983
+ AttributeTypeFamily = 0
984
+ AttributeTypeSize = 1
985
+ AttributeTypeWeight = 2
986
+ AttributeTypeItalic = 3
987
+ AttributeTypeStretch = 4
988
+ AttributeTypeColor = 5
989
+ AttributeTypeBackground = 6
990
+ AttributeTypeUnderline = 7
991
+ AttributeTypeUnderlineColor = 8
992
+ AttributeTypeFeatures = 9
993
+
994
+ You can probably use the rubygems features of comparing old gem
995
+ releases with one another to see which support has been added to
996
+ ruby-libui over the past months or so. Most work was probably done
997
+ in 2020 or even before that (there are some older ruby bindings
998
+ to libui, but these work differently as kojix2 explained).
999
+
1000
+ ## Limitations of/in LibUI
1001
+
1002
+ LibUI is not perfect - it is missing many things that should work just fine
1003
+ on the main operating systems. On top of that I'd love to support specific
1004
+ features on a given operating system even if it is NOT cross-platform. But
1005
+ this is not possible via LibUI.
1006
+
1007
+ The following subsection mentions a few constraints of LibUI in varying
1008
+ degrees of complexity/importance:
1009
+
1010
+ - No CSS support. I miss this. On ruby-gtk3 this is possible. In LibUI I can't.
1011
+ - Unable to set different fonts for an application other than using FontButton.
1012
+
1013
+ ## Using C structs via fiddle
1014
+
1015
+ To use C structs you may have to allocate memory.
1016
+
1017
+ Example:
1018
+
1019
+ font_descriptor = UI::FFI::FontDescriptor.malloc
1020
+ font_descriptor.to_ptr.free = Fiddle::RUBY_FREE
1021
+
1022
+ This is currently necessary. Hopefully Fiddle becomes
1023
+ a bit more convenient to use in the future, so we don't
1024
+ have to care about **malloc** when writing ruby anymore.
1025
+
1026
+ ## Structure of the project
1027
+
1028
+ In **September 2021** the libui_paradise project was re-arranged slightly. There
1029
+ is now a dedicated **libui_classes/** directory that "simulates" separate classes.
1030
+ For example, libui_button now resides in **button.rb**. I use the same directory
1031
+ layout in the **gtk_paradise** gem and I think it makes a lot of sense - at the
1032
+ least I can quickly find the code that I may want to modify or extend. If I
1033
+ want to extend buttons, then I modify **buttons.rb**. That's simple, right?
1034
+
1035
+ Another reason why the project was re-structured was so that we can use a
1036
+ **unified DSL** for the GUI elements in the long run.
1037
+
1038
+ For example, code such as the following:
1039
+
1040
+ _ = button
1041
+ _.on_clicked {
1042
+ puts 'Hello world!'
1043
+ }
1044
+
1045
+ Should work on ruby-gtk3, libui and all the other toolkits one day - even on
1046
+ the www. But the different toolkits do not implement the same functionality.
1047
+ Libui only offers a reduced functionality, for instance. Thus, the DSL that
1048
+ we may then use **can only support a subset of functionality**, whereas other
1049
+ toolkits are more useful in this regard. This was an additional reason why files
1050
+ such as button.rb were created - it makes it easier to know which functionality
1051
+ has to be changed in order for libui_paradise to enable such a unified
1052
+ DSL approach. This currently does not yet work fine in 2021, but one day it
1053
+ should work just fine.
1054
+
1055
+ Note that the older l**ibui_paradise gem** will be made available at the
1056
+ least for three months (so until end of December 2021 at the least). It used
1057
+ a different directory layout.
1058
+
1059
+ You may ask why the main module is called **LibuiParadise::Extensions**.
1060
+ Why not modify LibUI directly? After all the gtk_paradise gem does the
1061
+ same, by modifying **module Gtk** directly.
1062
+
1063
+ That is a good question, and the main reason for this is because I was
1064
+ not sure whether I can actually get away with modifying the LibUI
1065
+ namespace directly. Fiddle::Pointer still scares me, so I am experimenting.
1066
+
1067
+ For now I thought it best to create a separate module, that is then included
1068
+ and modified - see the **included hook** that is presently used. This seems
1069
+ to be simpler for the time being. I may plan to change a lot more one day,
1070
+ if I ever manage to find out how to simulate proper subclasses via
1071
+ Fiddle::Pointer ... :)
1072
+
1073
+ --------------------------------------------------------------------------------
1074
+ ## SNIPPETS.md
1075
+
1076
+ Next, the content of the file called **SNIPPETS.md** will be shown. Note
1077
+ that this file will eventually be integreated into this file, and then
1078
+ subsequently removed one day.
1079
+
1080
+ EMBED_THIS_FILE /home/x/programming/ruby/src/libui_paradise/doc/SNIPPETS.md
1081
+
1082
+ --------------------------------------------------------------------------------
1083
+
1084
+ ## Advantages and Disadvantages of the libui project
1085
+
1086
+ It would be unfair to only selectively name advantages but not talk about
1087
+ disadvantages, so this subsection will show some limitations, trade-offs,
1088
+ constraints and opportunities. This is not complete, but it may become
1089
+ somewhat more complete over time. Stay tuned.
1090
+
1091
+ (a) Advantages:
1092
+
1093
+ - Works on windows out-of-the-box after you installed the libui-gem.
1094
+ - Is super-simple to use compared to other toolkits, including ruby-gtk.
1095
+ - Super-simple to build up a prototype for a GUI, buttons that work,
1096
+ spin-boxes, text-views and so forth. Faster than any other toolkit
1097
+ IMO.
1098
+ - Works cross-platform.
1099
+
1100
+ (b) Disadvantage:
1101
+
1102
+ - Limited ability to control the layout and size of widgets.
1103
+ - May look like utter crap ... :-)
1104
+ - Some functionality is missing, such as a scrolled-window for every widget.
1105
+ - No way to use different fonts in the same application and choosing a font
1106
+ is needlessly complicated. (This may not be completely correct, though -
1107
+ the glimmer-dsl-libui has example that seem to work. But if you ask me
1108
+ right now in 2021 how this works via a standalone example then I can
1109
+ happily tell you I have absolutely no idea. Which brings me to the
1110
+ next problem...)
1111
+ - Lack of documentation. This part is REALLY annoying ...
1112
+
1113
+ Some more disadvantages relate to Fiddle::Pointer. You kind of need to
1114
+ know C fairly well as well as the GC in ruby, in order to understand
1115
+ what is going on. Since I don't, I hit a dead end, kind of.
1116
+
1117
+ This is so far in September 2021. Let's see what the future brings.
1118
+ Perhaps other toolkits will learn from libui and implement the good
1119
+ parts for **their own** widget set.
1120
+
1121
+ ## LibuiParadise.parse_this_config_file()
1122
+
1123
+ This method can be used to parse a .config file. This file should
1124
+ describe the main window, such as:
1125
+
1126
+ width: 1000
1127
+ height: 150
1128
+ title: Parse Config File Example
1129
+
1130
+ I have been using this in gtk_paradise and I think it may be convenient
1131
+ if you create lots of small windows and widgets that you don't want
1132
+ to hardcode values for directly into the .rb file at hand.
1133
+
1134
+ To create a toplevel window from this use code such as the following:
1135
+
1136
+ use_this_config_file = '023_parse_config_file_example.config'
1137
+ window = LibuiParadise.parse_this_config_file(use_this_config_file)
1138
+
1139
+ In fact, the example **023_parse_config_file_example.rb** shows how
1140
+ this is used. The width of the main window will be 1000 and the
1141
+ height only 150.
1142
+
1143
+ This functionality may be extended in the future. For example, we
1144
+ could enable to automatically parse such a .config file if it
1145
+ exists, thus not even requiring that method call in the future.
1146
+
1147
+ For now, though, you have to explicitely use that method if you
1148
+ want to instantiate such a window from a .config file. It will
1149
+ be evaluated at a later time (past September 2021) how useful this
1150
+ really is.
1151
+
1152
+ ## Width and Height of the main window
1153
+
1154
+ Different computers / monitors have different display values
1155
+ (their monitor).
1156
+
1157
+ I use a wide-screen LCD monitor, but I also have a smaller laptop,
1158
+ and I want **libui** to work there just as well.
1159
+
1160
+ The following API can be used to set the width and height of the
1161
+ main window:
1162
+
1163
+ set_height()
1164
+ set_width()
1165
+
1166
+ You can also use something like '95%' as input. In that case the
1167
+ desired value will be calculated depending on the max-resolution
1168
+ of the current display. This presently only works on **linux**; if
1169
+ someone knows how to make this work on windows and Mac OSX let
1170
+ me know. (On these systems it will instead default to a hardcoded
1171
+ value of 1024 for width and 800 for height).
1172
+
1173
+ The following example shows how to use a percentage value:
1174
+
1175
+ set_height('80%')
1176
+
1177
+ ## Coloured Text
1178
+
1179
+ At this point I only show how this may look on Win7, re-using
1180
+ the picture the **kotlin-libui developers** made available:
1181
+
1182
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/drawtext/drawtext-windows7.png" style="margin-left: 2em">
1183
+
1184
+ Doesn't look that bad, right? May not be the prettiest GUI of all
1185
+ times, but it is functional - and simple.
1186
+
1187
+ ## DSLs for libui (in ruby)
1188
+
1189
+ kojix2 pointed out that glimmer has support for libui; check it out
1190
+ here:
1191
+
1192
+ https://github.com/AndyObtiva/glimmer-dsl-libui
1193
+
1194
+ libui_paradise will support the same syntax (in the long run) as
1195
+ glimmer does, either directly, or via a module and a way to
1196
+ require it specifically. But stay tuned for this - right now as
1197
+ of late 2021 this is not yet guaranteed. (If anyone needs quick
1198
+ API changes, let me know and I'll change libui_paradise. Other
1199
+ than that, the libui_paradise project is in a slow maintenance
1200
+ mode right now, so again, stay tuned. \o/ )
1201
+
1202
+ ## Status of the libui_paradise project
1203
+
1204
+ I am still, as of 2021, experimenting somewhat randomly. While I intend to
1205
+ improve libui_paradise, it is a hobby project that is not as important
1206
+ as, say, the gtk_paradise project (which contains custom ruby-gtk
1207
+ related addons; ruby-gtk has a LOT more functionality than libui has,
1208
+ just look at the CSS-related aspects).
1209
+
1210
+ I can not promise to work reliably on libui_paradise, but every now and
1211
+ then, say every some weeks, I will try to add more code to it, in
1212
+ particular examples and documentation. However had, keep in mind that
1213
+ the project is not one of my most important projects, so it may not
1214
+ receive as many updates as other projects; Andy is much more active
1215
+ recently via glimmer-libui-dsl, so check out his project. See also
1216
+ the following subsection, the "Todo List".
1217
+
1218
+ ## Todo List and Goals related to libui
1219
+
1220
+ Here I will list some todo entries and related goals for libui or the
1221
+ libui_paradise gem - if anyone finds out how to solve some of these
1222
+ limitations, you are welcome to share!
1223
+
1224
+ This list may become outdated eventually, so assume that this has been
1225
+ valid at one point in time, but may no longer be absolutely "up to date"
1226
+ anymore.
1227
+
1228
+ Here goes:
1229
+
1230
+ - Add more small, **standalone examples** showcasing how to use (and
1231
+ combine) different functionality, in particular colourized text and use
1232
+ of (embedded) images if applicable. Ideally we should be able to
1233
+ combine both freely, if possible. Ideally it should be a small functionality,
1234
+ to demonstrate **how** something can be done. Users can then expand from
1235
+ this point for their own GUI sets.
1236
+
1237
+ - Find out how to create "proper" subclasses to Fiddle::Pointer, simulating
1238
+ a subclass to it.
1239
+
1240
+ - Find out how halign and valign work in Libui-Grid. Somehow I can not easily reposition it ...
1241
+ or perhaps I have been able to, and just have not noticed this (may have to embed one
1242
+ widget in another widget in order for this to come into effect)
1243
+
1244
+ - Find out how to do ad-hoc calls on linux to gtk, so that we can use CSS.
1245
+ Perhaps some extension mechanism could be used, similar to how ffi works
1246
+ in general. Unfortunately this probably requires knowledge of C, so this
1247
+ is an obstacle for me. I should have learned C before ruby ... :P
1248
+
1249
+ - Get more people to learn about libui and use them in their projects,
1250
+ in particular for simple applications. I think this is the most important
1251
+ goal: we need more people to learn about libui and begin to use it.
1252
+ Contribute to upstream as well. If we have a sufficiently large user
1253
+ base then it should be easier to add new possibilities onto libui,
1254
+ which in turn will "cascade" downwards to all the other bindings
1255
+ to libui, be it in kotlin, python, ruby and so forth.
1256
+
1257
+ ## DateTime Widget
1258
+
1259
+ Let's start with an image to show how the date-time picker may look
1260
+ on windows:
1261
+
1262
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/datetime/datetime-windows7.png" style="margin-left: 2em">
1263
+
1264
+ Here is another image, from Win10:
1265
+
1266
+ <img src="https://i.imgur.com/Vkn4Vmg.jpg" style="margin: 1em; margin-left: 2em">
1267
+
1268
+ The basic API is this:
1269
+
1270
+ LibUI.date_time_picker_time
1271
+
1272
+ Or rather:
1273
+
1274
+ date_time_picker = LibUI.new_date_time_picker
1275
+ time = LibUI::FFI::TM.malloc
1276
+ LibUI.date_time_picker_on_changed(date_time_picker) {
1277
+ LibUI.date_time_picker_time(date_time_picker, time)
1278
+ }
1279
+
1280
+ Modify this to suit your needs as-is.
1281
+
1282
+ I tested this in **February 2022** on Linux. The next screenshot
1283
+ shows how this may look (on icewm):
1284
+
1285
+ <img src="https://i.imgur.com/Jdbf6Jc.png" style="margin: 1em">
1286
+
1287
+ ## radio-buttons in LibUI
1288
+
1289
+ The syntax goes something like this:
1290
+
1291
+ rb = ui_radio_buttons
1292
+ UI.radio_buttons_append(rb, 'Radio Button 1')
1293
+ UI.radio_buttons_append(rb, 'Radio Button 2')
1294
+ UI.radio_buttons_append(rb, 'Radio Button 3')
1295
+ outer_vbox.minimal(rb) # add the radio-button control to the box.
1296
+
1297
+ In other words: you instantiate a new rb-radio-button 'pointer';
1298
+ and then you simply append the options onto it. The String above
1299
+ will be the option at hand, e. g. 'Radio Button 1'.
1300
+
1301
+ As I am very lazy I don't want this boilerplate code, so the following
1302
+ variant exists instead:
1303
+
1304
+ rb = ui_radio_buttons( ['Radio Button 1','Radio Button 2','Radio Button 3'] )
1305
+ outer_vbox.minimal(rb) # add the radio-button control to the box.
1306
+
1307
+ So you can simply pass in an Array. This cuts down the number of
1308
+ lines necessary. \o/
1309
+
1310
+ Here is a screenshot to show how this looks (from
1311
+ example **032_radio_button_example.rb**).
1312
+
1313
+ <img src="https://i.imgur.com/aJOBQzn.png" style="margin-left: 2em">
1314
+
1315
+ ## Password entries in LibUI
1316
+
1317
+ If you use the libui_paradise gem then you can create a new password entry
1318
+ in using any of the following variants:
1319
+
1320
+ entry = ui_password_entry
1321
+ entry = password_entry
1322
+
1323
+ For "raw" libui, use this:
1324
+
1325
+ LibUI.new_password_entry
1326
+
1327
+ ## Tables in LibUI
1328
+
1329
+ You may be able to create a new table via:
1330
+
1331
+ table = LibUI.new_table
1332
+
1333
+ model = LibUI.new_table_model(model_handler)
1334
+
1335
+ table_params = LibUI::FFI::TableParams.malloc
1336
+ table_params = Fiddle::RUBY_FREE
1337
+ table_params.Model = model
1338
+ table_params.RowBackgroundColorModelColumn = -1
1339
+ table = LibUI.new_table(table_params)
1340
+
1341
+ The table header is an array that contains the following attributes:
1342
+
1343
+ 1. editable, bool type, the column is whether editable
1344
+ 2. textColor
1345
+ 3. title
1346
+ 4. type, specify value of button, image, imgtext, progress, checkbox, checkboxtext, color, text
1347
+
1348
+ Note that this is incomplete; it's a bit complicated.
1349
+
1350
+ In the end, this is possible though:
1351
+
1352
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/table/table-windows7.png" style="margin-left: 2em">
1353
+
1354
+ ## Form example in LibuiParadise
1355
+
1356
+ Inspired by **glimmer-libui**, I ported the form example, into
1357
+ **027_form_example.rb**. Unfortunately it does not look
1358
+ as good as it does in **glimmer-libui**, and the code I use for
1359
+ it is too verbose right now.
1360
+
1361
+ Still, if you are curious, this is how it looks on icewm
1362
+ in October 2021:
1363
+
1364
+ <img src="https://i.imgur.com/FkU6aWd.png" style="margin-left: 2em">
1365
+
1366
+ I may improve this eventually a bit, so that the alignment looks
1367
+ as good as it does on glimmer-dsl; the code of 27_form_example.rb
1368
+ may also be cleaned up in the future (the glimmer-libui code
1369
+ looks better). But for now this is how it is; Andy is very
1370
+ actively improving **glimmer-libui** right now.
1371
+
1372
+ ## Available widgets in libUI:
1373
+
1374
+ Button A button with a label
1375
+ Checkbox A checkbox with a label
1376
+ Combobox A simple combobox
1377
+ ColorButton A button for selecting a color
1378
+ EditableCombobox A combobox that can be edited.
1379
+ Entry Text input, can be disabled.
1380
+ FontButton A button for selecting a font (Incomplete: Cannot set programmatically)
1381
+ Form A container that takes labels for its contents
1382
+ Grid A container that aligns widgets for window design
1383
+ Group A container that provides a title for a set of items
1384
+ Label Displays a single line of text
1385
+ Menu Creates a single column of an application menu
1386
+ MultilineEntry An entry that allows multiple lines.
1387
+ Time and Date Pickers Allows choosing of a date and/or time.
1388
+ ProgressBar Displays a progress bar
1389
+ RadioButton A set of radio buttons
1390
+ Separator A simple vertical or horizontal separator
1391
+ Slider A draggable slider for choosing a value in a range
1392
+ Spinbox A numerical input with a minimum and maximum range
1393
+ Tab A set named tabs for placing items in
1394
+ Window Contains any other widget, cannot be embedded in a container
1395
+ VBox, HBox A vertical or horizontal box for grouping items
1396
+
1397
+ Available "**new**"-widgets in LibUI:
1398
+
1399
+ LibUI.new_area
1400
+ LibUI.new_attributed_string
1401
+ LibUI.new_group
1402
+ LibUI.new_spinbox
1403
+ LibUI.new_stretch_attribute
1404
+ LibUI.new_background_attribute
1405
+ LibUI.new_button # this is a simple button
1406
+ LibUI.new_checkbox # this is a simple checkbox
1407
+ LibUI.new_color_attribute
1408
+ LibUI.new_color_button
1409
+ LibUI.new_combobox # this is a combobox
1410
+ LibUI.new_date_picker
1411
+ LibUI.new_date_time_picker
1412
+ LibUI.new_editable_combobox
1413
+ LibUI.new_grid
1414
+ LibUI.new_horizontal_box
1415
+ LibUI.new_horizontal_separator # this is a simple horizontal separator
1416
+ LibUI.new_image # this is a simple image
1417
+ LibUI.new_italic_attribute # this is basically italic font-style
1418
+ LibUI.new_label
1419
+ LibUI.new_tab
1420
+ LibUI.new_table # this is a simple table
1421
+ LibUI.new_table_model
1422
+ LibUI.new_table_value_image
1423
+ LibUI.new_table_value_int
1424
+ LibUI.new_table_value_color
1425
+ LibUI.new_table_value_string
1426
+ LibUI.new_time_picker
1427
+ LibUI.new_menu
1428
+ LibUI.new_multiline_entry # this is a textview
1429
+ LibUI.new_non_wrapping_multiline_entry
1430
+ LibUI.new_open_type_features
1431
+ LibUI.new_password_entry
1432
+ LibUI.new_underline_attribute
1433
+ LibUI.new_entry
1434
+ LibUI.new_progress_bar # this is a progress_bar
1435
+ LibUI.new_underline_color_attribute
1436
+ LibUI.new_family_attribute
1437
+ LibUI.new_radio_buttons # this is for the radio button
1438
+ LibUI.new_vertical_box
1439
+ LibUI.new_features_attribute
1440
+ LibUI.new_scrolling_area # this is a scrolling area
1441
+ LibUI.new_vertical_separator
1442
+ LibUI.new_font_button
1443
+ LibUI.new_weight_attribute
1444
+ LibUI.new_form # this is a form
1445
+ LibUI.new_size_attribute
1446
+ LibUI.new_window
1447
+ LibUI.new_slider # this is a slider
1448
+
1449
+ ## A search entry in LibUI
1450
+
1451
+ The API is:
1452
+
1453
+ LibUI.new_search_entry # this is a search entry
1454
+
1455
+ How does this look? The following image shows this:
1456
+
1457
+ <img src="https://i.imgur.com/AueDc7l.png" style="margin: 1em">
1458
+
1459
+ ## Creating a new image in LibUI:
1460
+
1461
+ Use the following API for this:
1462
+
1463
+ image = LibUI.new_image(width, height)
1464
+
1465
+ You can use external libraries to determine the width
1466
+ and height of an image, such as ChunkyPNG.
1467
+
1468
+ Example for this:
1469
+
1470
+ canvas = ChunkyPNG::Canvas.from_io(this_file)
1471
+ width = canvas.width
1472
+ height = canvas.height
1473
+
1474
+ ## Creating a new drawing area
1475
+
1476
+ This should suffice:
1477
+
1478
+ handler = LibUI::FFI::AreaHandler.malloc
1479
+ handler.to_ptr.free = Fiddle::RUBY_FREE
1480
+ histogram = LibUI.new_area(handler)
1481
+
1482
+ ## Horizontal boxes
1483
+
1484
+ A HBox represents a horizontal box. The API for putting
1485
+ something into a hbox goes as follows, such as when you
1486
+ wish to put a text-entry into the hbox:
1487
+
1488
+ LibUI.box_append(hbox1, text_entry, 1)
1489
+
1490
+ ## The slider widget
1491
+
1492
+ If you use the LibuiParadise gem then you can create and use a new slider
1493
+ like this:
1494
+
1495
+ slider = ui_slider
1496
+
1497
+ # define our callback
1498
+ slider_changed_callback = proc { |ptr|
1499
+ puts "New Slider value: #{UI.slider_value(ptr)}"
1500
+ 0
1501
+ }
1502
+ UI.slider_on_changed(slider, slider_changed_callback) # last element is nil, but it seems we can omit it
1503
+
1504
+ This may look like so on Linux:
1505
+
1506
+ <img src="https://i.imgur.com/GVKPMS7.png" style="margin-left: 3em">
1507
+
1508
+ ## Create a new tabbed notebook:
1509
+
1510
+ ui_tabs
1511
+ ui_notebook
1512
+
1513
+ ## Creating a textview in LibUI:
1514
+
1515
+ First, let's have a look how this may look:
1516
+
1517
+ <img src="https://i.imgur.com/v287z2k.png" style="margin: 1em">
1518
+
1519
+ Next, here is the toplevel API to create a new multiline entry:
1520
+
1521
+ LibUI.new_non_wrapping_multiline_entry
1522
+
1523
+ If you want to make it read only then you can use an API like this:
1524
+
1525
+ LibUI.multiline_entry_set_read_only(text, 1)
1526
+
1527
+ If you make use of the LibuiParadise gem then the above can be
1528
+ simplified to:
1529
+
1530
+ text = multiline_entry('Hello world! This can be set to read only via .is_read_only().')
1531
+ text.is_read_only # Now it is read-only.
1532
+
1533
+ ## Coloured box examples
1534
+
1535
+ Since as of November 2021 you can use the libui_paradise gem to
1536
+ create coloured boxes:
1537
+
1538
+ <img src="https://i.imgur.com/Ervg3vF.png" style="margin: 0.5em; margin-left: 2em">
1539
+
1540
+ The API for this goes like so:
1541
+
1542
+ LibuiParadise.draw_rectangle
1543
+ LibuiParadise.draw_rectangle(50, 50, :steelblue) # width, height, html-colour to use
1544
+
1545
+ ## Attributed Strings in LibUI
1546
+
1547
+ An **AttributedString** is a string that contains information
1548
+ about specific styles, such as text color, font, font size. It can be
1549
+ drawn in a **UiArea element**.
1550
+
1551
+ Various ways to style such an attributed String is possible.
1552
+
1553
+ For instance, the text-weight part accepts these values
1554
+
1555
+ minimum
1556
+ thin
1557
+ ultraLight
1558
+ light
1559
+ book
1560
+ normal
1561
+ medium
1562
+ semiBold
1563
+ bold
1564
+ ultraBold
1565
+ heavy
1566
+ ultraHeavy
1567
+ maximum
1568
+ # or any number between minimum and maximum
1569
+
1570
+ To create an attributed String you can use the following API:
1571
+
1572
+ string = LibUI.new_attributed_string
1573
+ attributes = LibUI.new_family_attribute("Courier New 30") # Specify a certain font.
1574
+ attribute = LibUI.new_color_attribute(0.75, 0.25, 0.5, 0.75) # And a certain colour.
1575
+ LibUI.append_with_attribute("text color", attribute, nil)
1576
+
1577
+ ## Using fonts in LibUI:
1578
+
1579
+ font_descriptor = UI::FFI::FontDescriptor.malloc
1580
+ p 'Font family: '+font_descriptor.Family.to_s+
1581
+ 'Font size: '+font_descriptor.Size+
1582
+ 'Font weight: '+font_descriptor.Weight+
1583
+ 'Font italic: '+font_descriptor.Italic+
1584
+ 'Font stretch: '+font_descriptor.Stretch
1585
+
1586
+ ## How to run the main loop in libui
1587
+
1588
+ LibUI.main
1589
+
1590
+ ## Examples distributed in the libui_paradise gem
1591
+
1592
+ In February 2022 the examples directory was re-arranged. There are now two
1593
+ different directories, called simple/ and complex/.
1594
+
1595
+ Files in the simple/ directory may only use code that is made available
1596
+ via kojix2' libui bindings.
1597
+
1598
+ Files in the complex/ directory may tap into code made available by the
1599
+ libui_paradise gem.
1600
+
1601
+ This re-arrangement was done because some of the old examples were no
1602
+ longer working, and it was messy to try to find out whether there was
1603
+ a bug in the libui bindings, or whether I messed up. That way I can
1604
+ now focus on the complex/ examples to showcase what libui_paradise
1605
+ can do, whereas the simple/ directory will show how you can work with
1606
+ "vanilla" libui in general.
1607
+
1608
+ ## Setting the title, width and height of a widget
1609
+
1610
+ The libui_paradise gem comes with methods such as
1611
+ <b>.set_title()</b>, <b>.set_width()</b> and
1612
+ <b>.set_height()</b>.
1613
+
1614
+ This is a bit cumbersome to use as you need at the least three lines
1615
+ of code normally, as well as the constants. So the following compound
1616
+ method was added in **February** of **2022**:
1617
+
1618
+ title_width_height()
1619
+
1620
+ Now you can define title, width and height of your main window
1621
+ via one method call. \o/
1622
+
1623
+ ## Click-actions for buttons
1624
+
1625
+ This subsection just shows some code how main libui handles
1626
+ click actions on buttons.
1627
+
1628
+ LibUI.button_on_clicked(button) {
1629
+ LibUI.msg_box(main_window, 'Information', 'You clicked the button')
1630
+ 0
1631
+ }
1632
+
1633
+ ## libui-ng
1634
+
1635
+ In **2022**, some developers contributed patches to **libui**. This is
1636
+ **work-in-progress**.
1637
+
1638
+ Some API was added or modified, such as:
1639
+
1640
+ uiTableColumnWidth() API
1641
+ uiTableColumnSetWidth() API
1642
+ uiComboboxNumItems() API
1643
+ uiOpenFolder() API
1644
+ uiSliderHasToolTip() API
1645
+ uiSliderSetHasToolTip() API
1646
+ uiSliderSetRange() API
1647
+ uiTableHeaderSortIndicator() API
1648
+ uiTableHeaderSetSortIndicator() API
1649
+ uiTableHeaderOnClicked() API
1650
+ uiTableHeaderVisible() API
1651
+ uiTableHeaderSetVisible() API
1652
+ uiBoxNumChildren() API
1653
+ uiFormNumChildren() API
1654
+ uiComboboxInsertAt() API
1655
+ uiComboboxDelete() API
1656
+ uiComboboxClear() API
1657
+ uiWindowResizeable() API
1658
+ uiWindowSetResizeable() API
1659
+ uiDrawPathEnded() API
1660
+ uiFreeFontDescriptor() API
1661
+ uiLoadControlFont() API
1662
+
1663
+ Not all of this is supported in kojix2' libui as of
1664
+ yet, but it may be supported eventually.
1665
+
1666
+ ## Links related to libui or libui-based projects
1667
+
1668
+ This subsection may contain a few links, in the event that other
1669
+ people want to see useful entries.
1670
+
1671
+ I will try to explain what is to be seen by these various pages.
1672
+
1673
+ (1) https://wiki.call-cc.org/eggref/4/libui
1674
+
1675
+ This is using chicken-egg scheme for bindings to libui. I like the
1676
+ simplicity and overview - it's really nice to read and use. I'd
1677
+ wish we would have this for ruby too. :)
1678
+
1679
+ Note that the documentation is outdated as of 2021, though. A fate
1680
+ shared with a lot of documentation in general out there ...
1681
+
1682
+ (2) https://pkg.go.dev/github.com/andlabs/ui
1683
+
1684
+ This is andlabs' documentation for libui, from the point of view of
1685
+ **Go**. It is probably the biggest, most complete documentation for
1686
+ libui. While it is specific to Go, as it was written by the same
1687
+ author you can expect the documentation to be quite decent.
1688
+
1689
+ (Note that some other bindings contain good documentation too,
1690
+ such as kotlin libui bindings and others.)
1691
+
1692
+ (3) http://api.call-cc.org/4/doc/libui
1693
+
1694
+ Similar to the first one, but uses a different layout, which
1695
+ may be helpful.
1696
+
1697
+ ## Grids in LibUI
1698
+
1699
+ The API for creating a new grid in libui is quite complex and
1700
+ hard to remember:
1701
+
1702
+ LibUI.grid_append(grid, entry1, 0, 0, 2, 1, 0, 0, 1, 0)
1703
+ LibUI.grid_append(grid, text('Yo2'), 1, 0, 1, 1, 0, 0, 1, 0) # text() can be used if you use the libui_paradise gem
1704
+
1705
+ Who can remember offhand what these values mean, though?
1706
+
1707
+ The upstream struct is this:
1708
+
1709
+ struct gridChild {
1710
+ uiControl *c;
1711
+ int left;
1712
+ int top;
1713
+ int xspan;
1714
+ int yspan;
1715
+ int hexpand;
1716
+ uiAlign halign;
1717
+ int vexpand;
1718
+ uiAlign valign;
1719
+
1720
+ // have these here so they don't need to be reallocated each relayout
1721
+ int finalx, finaly;
1722
+ int finalwidth, finalheight;
1723
+ int minwidth, minheight;
1724
+ };
1725
+
1726
+ The documentation for Go has this signature:
1727
+
1728
+ func (g *Grid) Append(child Control, left, top int, xspan, yspan int, hexpand bool, halign Align, vexpand bool, valign Align)
1729
+
1730
+ Append adds the given control to the Grid, at the given coordinate.
1731
+
1732
+ I assume that **uiControl c** refers to the widget that is to be embedded
1733
+ into the grid, so the numbers that follow afterwards are the ones
1734
+ that are important. Let's have a look at them, based on the above API
1735
+ call, and only list these again, without the **()**:
1736
+
1737
+ # left, top, xspan, yspan, hexpand, halign, vexpand, valign
1738
+ # 0, 0, 2, 1, 0, 0, 1, 0
1739
+
1740
+ xspan and yspan refer to width; left and top refer to the position
1741
+ in the grid. hexpand and vexpand means whether the grid-cell will
1742
+ expand horizontally or vertically. halign and valign, I think,
1743
+ are used to align the grid-cell horizontally and vertically, so you
1744
+ can position them exactly in the middle.
1745
+
1746
+ Recently (August 2021) I discovered that you can actually put
1747
+ a button-in-a-button. I don't know whether this is a bug or
1748
+ a feature, but it is hilarious.
1749
+
1750
+ The 'raw' code I used for this was the following:
1751
+
1752
+ UI.grid_append(grid, UI.new_button('3'),0,1,1,1,1,1,1,1)
1753
+ UI.grid_append(grid, UI.new_button('4'),1,1,1,1,1,1,1,1)
1754
+ UI.grid_append(grid, UI.new_button('5'),0,1,1,1,1,0,1,0)
1755
+ UI.grid_append(grid, UI.new_button('6'),1,1,1,1,1,0,1,0)
1756
+
1757
+ This led to the following image:
1758
+
1759
+ <img src="https://i.imgur.com/6sWwWKh.png" style="margin-left: 1em">
1760
+
1761
+ The smaller buttons and the larger buttons can be clicked. They
1762
+ reside in the same grid-cell. I don't know whether this is a
1763
+ bug or a feature really, but this was quite hilarious to see.
1764
+ In the event that I may forget this, I'll keep this here as
1765
+ a reference.
1766
+
1767
+ If you want to pad the grid you can use the following method:
1768
+
1769
+ LibUI.grid_set_padded(grid, 25)
1770
+
1771
+ You can specify the alignment for an individual grid. For
1772
+ instance, via <b>uiAlignCenter</b> you can align a grid
1773
+ to the center. (It seems as if uiAlignCenter is only
1774
+ used in a grid, though, so it seems as if we can not
1775
+ align it elsewhere.)
1776
+
1777
+ In <b>August 2022</b> I got fed up with the API, so I
1778
+ added a new API to Libui-grid using a Hash.
1779
+
1780
+ The available keywords are, to keep it simple, the
1781
+ following:
1782
+
1783
+ left
1784
+ top
1785
+ xspan
1786
+ yspan
1787
+ hexpand
1788
+ halign
1789
+ vexpand
1790
+ valign
1791
+
1792
+ The file <b>examples/complex/016_grid_example.rb</b> shows
1793
+ this new API, mixed with the old, original API.
1794
+
1795
+ Usage example for the new API:
1796
+
1797
+ grid = libui_grid
1798
+ grid.hash_grid(
1799
+ text('Yo7'),
1800
+ { left: 2, top: 3, xspan: 3, yspan: 3, hexpand: 0, halign: 0, vexpand: 0, valign: 0 }
1801
+ )
1802
+
1803
+ In the closing days of <b>August 2022</b> I went on
1804
+ to improve the above. Three new methods were "added"
1805
+ to grid (actually Fiddle::Pointer, but hopefully one
1806
+ day I can find out how to work on a grid directly
1807
+ in libui; right now I seem to only have to work with
1808
+ raw pointers, which confuses me).
1809
+
1810
+ These three methods are:
1811
+
1812
+ .left()
1813
+ .right()
1814
+ .new_line()
1815
+
1816
+ So, let's picture a grid. The very first cell is on the
1817
+ top left, so we can use .left() to add a widget.
1818
+
1819
+ For instance:
1820
+
1821
+ grid.left(text('Hello world!'))
1822
+
1823
+ Then we can add more elements, and for doing so we will
1824
+ use .right() such as in:
1825
+
1826
+ grid.right(text('There is more to come!'))
1827
+ grid.right(text('Still more'))
1828
+
1829
+ Once you want to move to the next row, you have to use:
1830
+
1831
+ grid.new_line()
1832
+
1833
+ This is still not ideal, but for the time being it
1834
+ has to suffice. Note how much simpler it is compared
1835
+ to .attach() or .hash_grid().
1836
+
1837
+ Not every value can be set right now; at a later time
1838
+ that API may become more flexible. But for now this
1839
+ has to suffice.
1840
+
1841
+ ## Progress Bars in LibUI
1842
+
1843
+ Here an image how this may look:
1844
+
1845
+ <img src="https://i.imgur.com/i1i4ppZ.png" style="margin-left: 2em">
1846
+
1847
+ If you use the LibuiParadise project then you can create a new
1848
+ instance of a progress bar via <b>wrapper_new_progress_bar</b>.
1849
+
1850
+ Example:
1851
+
1852
+ progress_bar = wrapper_new_progress_bar
1853
+
1854
+ To then set a new value, use:
1855
+
1856
+ progress_bar.set_value()
1857
+ progress_bar.set_value(50) # will be 50%
1858
+
1859
+ You can then query this value via:
1860
+
1861
+ @progress_bar.value? # This will return a String, though.
1862
+
1863
+ ## Entries in LibUI (ui_entry)
1864
+
1865
+ The official source for an <b>ui_entry</b> for UNIX (including Linux systems)
1866
+ can be read here:
1867
+
1868
+ https://github.com/libui-ng/libui-ng/blob/master/unix/entry.c
1869
+
1870
+ Personally I tend to <b>create a new entry element</b> in this way:
1871
+
1872
+ entry = ui_entry
1873
+
1874
+ # or, if it shall be more GUI-agnostic, I do this:
1875
+
1876
+ entry1 = entry # where entry is entry() actually, and works for many GUI toolkits then
1877
+
1878
+ To <b>set new content</b> to such an entry, do make use
1879
+ of the following method call:
1880
+
1881
+ entry.set_text('foobar') # Make sure it is a String; that seems to work better.
1882
+
1883
+ To <b>query</b> the text content of such an entry, you can use the following API,
1884
+ if you use the libui_paradise gem:
1885
+
1886
+ entry.text?
1887
+
1888
+ To respond to events look at the **OnChanged** event.
1889
+
1890
+ To see a specific example where the user may input a password entry, have
1891
+ a look at the following file that is distributed within the libui_paradise
1892
+ gem:
1893
+
1894
+ libui_paradise/examples/complex/012_password_entry_example.rb
1895
+
1896
+ An entry can be <b>set read only</b> via:
1897
+
1898
+ entry.read_only # This has not been added into libui-paradise yet.
1899
+
1900
+ To <b>respond</b> to <b>on-changed events</b>, you can use:
1901
+
1902
+ LibUI.entry_on_changed()
1903
+
1904
+ A more complete example of this:
1905
+
1906
+ text_changed_callback = proc { |ptr|
1907
+ puts "Current textbox data: '#{UI.entry_text(ptr)}'"
1908
+ }
1909
+
1910
+ text_entry = ui_entry
1911
+ text_entry.set_text('Please enter a command')
1912
+ text_entry.on_changed {
1913
+ text_changed_callback
1914
+ }
1915
+
1916
+ The Proc object has to be passed into the {} block variation.
1917
+
1918
+ The latter uses:
1919
+
1920
+ LibUI.entry_on_changed(text_entry, text_changed_callback, nil)
1921
+
1922
+ If you use the libui_paradise gem, and only need to obtain the
1923
+ (changed) text of an entry in LibUI, then you can use the
1924
+ following simplified method call instead:
1925
+
1926
+ entry = ui_entry
1927
+ entry.on_changed {
1928
+ puts 'The text is now: '+entry.text?
1929
+ }
1930
+
1931
+ ADD_CONTACT_INFORMATION