libui_paradise 0.2.48

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of libui_paradise might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2064 -0
  3. data/doc/README.gen +1923 -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 +207 -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 +1157 -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 +1610 -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 +19 -0
  79. metadata +156 -0
data/doc/README.gen ADDED
@@ -0,0 +1,1923 @@
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
+ ## Horizontal boxes
1475
+
1476
+ A HBox represents a horizontal box. The API for putting
1477
+ something into a hbox goes as follows, such as when you
1478
+ wish to put a text-entry into the hbox:
1479
+
1480
+ LibUI.box_append(hbox1, text_entry, 1)
1481
+
1482
+ ## The slider widget
1483
+
1484
+ If you use the LibuiParadise gem then you can create and use a new slider
1485
+ like this:
1486
+
1487
+ slider = ui_slider
1488
+
1489
+ # define our callback
1490
+ slider_changed_callback = proc { |ptr|
1491
+ puts "New Slider value: #{UI.slider_value(ptr)}"
1492
+ 0
1493
+ }
1494
+ UI.slider_on_changed(slider, slider_changed_callback) # last element is nil, but it seems we can omit it
1495
+
1496
+ This may look like so on Linux:
1497
+
1498
+ <img src="https://i.imgur.com/GVKPMS7.png" style="margin-left: 3em">
1499
+
1500
+ ## Create a new tabbed notebook:
1501
+
1502
+ ui_tabs
1503
+ ui_notebook
1504
+
1505
+ ## Creating a textview in LibUI:
1506
+
1507
+ First, let's have a look how this may look:
1508
+
1509
+ <img src="https://i.imgur.com/v287z2k.png" style="margin: 1em">
1510
+
1511
+ Next, here is the toplevel API to create a new multiline entry:
1512
+
1513
+ LibUI.new_non_wrapping_multiline_entry
1514
+
1515
+ If you want to make it read only then you can use an API like this:
1516
+
1517
+ LibUI.multiline_entry_set_read_only(text, 1)
1518
+
1519
+ If you make use of the LibuiParadise gem then the above can be
1520
+ simplified to:
1521
+
1522
+ text = multiline_entry('Hello world! This can be set to read only via .is_read_only().')
1523
+ text.is_read_only # Now it is read-only.
1524
+
1525
+ ## Coloured box examples
1526
+
1527
+ Since as of November 2021 you can use the libui_paradise gem to
1528
+ create coloured boxes:
1529
+
1530
+ <img src="https://i.imgur.com/Ervg3vF.png" style="margin: 0.5em; margin-left: 2em">
1531
+
1532
+ The API for this goes like so:
1533
+
1534
+ LibuiParadise.draw_rectangle
1535
+ LibuiParadise.draw_rectangle(50, 50, :steelblue) # width, height, html-colour to use
1536
+
1537
+ ## Attributed Strings in LibUI
1538
+
1539
+ An **AttributedString** is a string that contains information
1540
+ about specific styles, such as text color, font, font size. It can be
1541
+ drawn in a **UiArea element**.
1542
+
1543
+ Various ways to style such an attributed String is possible.
1544
+
1545
+ For instance, the text-weight part accepts these values
1546
+
1547
+ minimum
1548
+ thin
1549
+ ultraLight
1550
+ light
1551
+ book
1552
+ normal
1553
+ medium
1554
+ semiBold
1555
+ bold
1556
+ ultraBold
1557
+ heavy
1558
+ ultraHeavy
1559
+ maximum
1560
+ # or any number between minimum and maximum
1561
+
1562
+ To create an attributed String you can use the following API:
1563
+
1564
+ string = LibUI.new_attributed_string
1565
+ attributes = LibUI.new_family_attribute("Courier New 30") # Specify a certain font.
1566
+ attribute = LibUI.new_color_attribute(0.75, 0.25, 0.5, 0.75) # And a certain colour.
1567
+ LibUI.append_with_attribute("text color", attribute, nil)
1568
+
1569
+ ## Using fonts in LibUI:
1570
+
1571
+ font_descriptor = UI::FFI::FontDescriptor.malloc
1572
+ p 'Font family: '+font_descriptor.Family.to_s+
1573
+ 'Font size: '+font_descriptor.Size+
1574
+ 'Font weight: '+font_descriptor.Weight+
1575
+ 'Font italic: '+font_descriptor.Italic+
1576
+ 'Font stretch: '+font_descriptor.Stretch
1577
+
1578
+ ## How to run the main loop in libui
1579
+
1580
+ LibUI.main
1581
+
1582
+ ## Examples distributed in the libui_paradise gem
1583
+
1584
+ In February 2022 the examples directory was re-arranged. There are now two
1585
+ different directories, called simple/ and complex/.
1586
+
1587
+ Files in the simple/ directory may only use code that is made available
1588
+ via kojix2' libui bindings.
1589
+
1590
+ Files in the complex/ directory may tap into code made available by the
1591
+ libui_paradise gem.
1592
+
1593
+ This re-arrangement was done because some of the old examples were no
1594
+ longer working, and it was messy to try to find out whether there was
1595
+ a bug in the libui bindings, or whether I messed up. That way I can
1596
+ now focus on the complex/ examples to showcase what libui_paradise
1597
+ can do, whereas the simple/ directory will show how you can work with
1598
+ "vanilla" libui in general.
1599
+
1600
+ ## Setting the title, width and height of a widget
1601
+
1602
+ The libui_paradise gem comes with methods such as
1603
+ <b>.set_title()</b>, <b>.set_width()</b> and
1604
+ <b>.set_height()</b>.
1605
+
1606
+ This is a bit cumbersome to use as you need at the least three lines
1607
+ of code normally, as well as the constants. So the following compound
1608
+ method was added in **February** of **2022**:
1609
+
1610
+ title_width_height()
1611
+
1612
+ Now you can define title, width and height of your main window
1613
+ via one method call. \o/
1614
+
1615
+ ## Click-actions for buttons
1616
+
1617
+ This subsection just shows some code how main libui handles
1618
+ click actions on buttons.
1619
+
1620
+ LibUI.button_on_clicked(button) {
1621
+ LibUI.msg_box(main_window, 'Information', 'You clicked the button')
1622
+ 0
1623
+ }
1624
+
1625
+ ## libui-ng
1626
+
1627
+ In **2022**, some developers contributed patches to **libui**. This is
1628
+ **work-in-progress**.
1629
+
1630
+ Some API was added or modified, such as:
1631
+
1632
+ uiTableColumnWidth() API
1633
+ uiTableColumnSetWidth() API
1634
+ uiComboboxNumItems() API
1635
+ uiOpenFolder() API
1636
+ uiSliderHasToolTip() API
1637
+ uiSliderSetHasToolTip() API
1638
+ uiSliderSetRange() API
1639
+ uiTableHeaderSortIndicator() API
1640
+ uiTableHeaderSetSortIndicator() API
1641
+ uiTableHeaderOnClicked() API
1642
+ uiTableHeaderVisible() API
1643
+ uiTableHeaderSetVisible() API
1644
+ uiBoxNumChildren() API
1645
+ uiFormNumChildren() API
1646
+ uiComboboxInsertAt() API
1647
+ uiComboboxDelete() API
1648
+ uiComboboxClear() API
1649
+ uiWindowResizeable() API
1650
+ uiWindowSetResizeable() API
1651
+ uiDrawPathEnded() API
1652
+ uiFreeFontDescriptor() API
1653
+ uiLoadControlFont() API
1654
+
1655
+ Not all of this is supported in kojix2' libui as of
1656
+ yet, but it may be supported eventually.
1657
+
1658
+ ## Links related to libui or libui-based projects
1659
+
1660
+ This subsection may contain a few links, in the event that other
1661
+ people want to see useful entries.
1662
+
1663
+ I will try to explain what is to be seen by these various pages.
1664
+
1665
+ (1) https://wiki.call-cc.org/eggref/4/libui
1666
+
1667
+ This is using chicken-egg scheme for bindings to libui. I like the
1668
+ simplicity and overview - it's really nice to read and use. I'd
1669
+ wish we would have this for ruby too. :)
1670
+
1671
+ Note that the documentation is outdated as of 2021, though. A fate
1672
+ shared with a lot of documentation in general out there ...
1673
+
1674
+ (2) https://pkg.go.dev/github.com/andlabs/ui
1675
+
1676
+ This is andlabs' documentation for libui, from the point of view of
1677
+ **Go**. It is probably the biggest, most complete documentation for
1678
+ libui. While it is specific to Go, as it was written by the same
1679
+ author you can expect the documentation to be quite decent.
1680
+
1681
+ (Note that some other bindings contain good documentation too,
1682
+ such as kotlin libui bindings and others.)
1683
+
1684
+ (3) http://api.call-cc.org/4/doc/libui
1685
+
1686
+ Similar to the first one, but uses a different layout, which
1687
+ may be helpful.
1688
+
1689
+ ## Grids in LibUI
1690
+
1691
+ The API for creating a new grid in libui is quite complex and
1692
+ hard to remember:
1693
+
1694
+ LibUI.grid_append(grid, entry1, 0, 0, 2, 1, 0, 0, 1, 0)
1695
+ 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
1696
+
1697
+ Who can remember offhand what these values mean, though?
1698
+
1699
+ The upstream struct is this:
1700
+
1701
+ struct gridChild {
1702
+ uiControl *c;
1703
+ int left;
1704
+ int top;
1705
+ int xspan;
1706
+ int yspan;
1707
+ int hexpand;
1708
+ uiAlign halign;
1709
+ int vexpand;
1710
+ uiAlign valign;
1711
+
1712
+ // have these here so they don't need to be reallocated each relayout
1713
+ int finalx, finaly;
1714
+ int finalwidth, finalheight;
1715
+ int minwidth, minheight;
1716
+ };
1717
+
1718
+ The documentation for Go has this signature:
1719
+
1720
+ func (g *Grid) Append(child Control, left, top int, xspan, yspan int, hexpand bool, halign Align, vexpand bool, valign Align)
1721
+
1722
+ Append adds the given control to the Grid, at the given coordinate.
1723
+
1724
+ I assume that **uiControl c** refers to the widget that is to be embedded
1725
+ into the grid, so the numbers that follow afterwards are the ones
1726
+ that are important. Let's have a look at them, based on the above API
1727
+ call, and only list these again, without the **()**:
1728
+
1729
+ # left, top, xspan, yspan, hexpand, halign, vexpand, valign
1730
+ # 0, 0, 2, 1, 0, 0, 1, 0
1731
+
1732
+ xspan and yspan refer to width; left and top refer to the position
1733
+ in the grid. hexpand and vexpand means whether the grid-cell will
1734
+ expand horizontally or vertically. halign and valign, I think,
1735
+ are used to align the grid-cell horizontally and vertically, so you
1736
+ can position them exactly in the middle.
1737
+
1738
+ Recently (August 2021) I discovered that you can actually put
1739
+ a button-in-a-button. I don't know whether this is a bug or
1740
+ a feature, but it is hilarious.
1741
+
1742
+ The 'raw' code I used for this was the following:
1743
+
1744
+ UI.grid_append(grid, UI.new_button('3'),0,1,1,1,1,1,1,1)
1745
+ UI.grid_append(grid, UI.new_button('4'),1,1,1,1,1,1,1,1)
1746
+ UI.grid_append(grid, UI.new_button('5'),0,1,1,1,1,0,1,0)
1747
+ UI.grid_append(grid, UI.new_button('6'),1,1,1,1,1,0,1,0)
1748
+
1749
+ This led to the following image:
1750
+
1751
+ <img src="https://i.imgur.com/6sWwWKh.png" style="margin-left: 1em">
1752
+
1753
+ The smaller buttons and the larger buttons can be clicked. They
1754
+ reside in the same grid-cell. I don't know whether this is a
1755
+ bug or a feature really, but this was quite hilarious to see.
1756
+ In the event that I may forget this, I'll keep this here as
1757
+ a reference.
1758
+
1759
+ If you want to pad the grid you can use the following method:
1760
+
1761
+ LibUI.grid_set_padded(grid, 25)
1762
+
1763
+ You can specify the alignment for an individual grid. For
1764
+ instance, via <b>uiAlignCenter</b> you can align a grid
1765
+ to the center. (It seems as if uiAlignCenter is only
1766
+ used in a grid, though, so it seems as if we can not
1767
+ align it elsewhere.)
1768
+
1769
+ In <b>August 2022</b> I got fed up with the API, so I
1770
+ added a new API to Libui-grid using a Hash.
1771
+
1772
+ The available keywords are, to keep it simple, the
1773
+ following:
1774
+
1775
+ left
1776
+ top
1777
+ xspan
1778
+ yspan
1779
+ hexpand
1780
+ halign
1781
+ vexpand
1782
+ valign
1783
+
1784
+ The file <b>examples/complex/016_grid_example.rb</b> shows
1785
+ this new API, mixed with the old, original API.
1786
+
1787
+ Usage example for the new API:
1788
+
1789
+ grid = libui_grid
1790
+ grid.hash_grid(
1791
+ text('Yo7'),
1792
+ { left: 2, top: 3, xspan: 3, yspan: 3, hexpand: 0, halign: 0, vexpand: 0, valign: 0 }
1793
+ )
1794
+
1795
+ In the closing days of <b>August 2022</b> I went on
1796
+ to improve the above. Three new methods were "added"
1797
+ to grid (actually Fiddle::Pointer, but hopefully one
1798
+ day I can find out how to work on a grid directly
1799
+ in libui; right now I seem to only have to work with
1800
+ raw pointers, which confuses me).
1801
+
1802
+ These three methods are:
1803
+
1804
+ .left()
1805
+ .right()
1806
+ .new_line()
1807
+
1808
+ So, let's picture a grid. The very first cell is on the
1809
+ top left, so we can use .left() to add a widget.
1810
+
1811
+ For instance:
1812
+
1813
+ grid.left(text('Hello world!'))
1814
+
1815
+ Then we can add more elements, and for doing so we will
1816
+ use .right() such as in:
1817
+
1818
+ grid.right(text('There is more to come!'))
1819
+ grid.right(text('Still more'))
1820
+
1821
+ Once you want to move to the next row, you have to use:
1822
+
1823
+ grid.new_line()
1824
+
1825
+ This is still not ideal, but for the time being it
1826
+ has to suffice. Note how much simpler it is compared
1827
+ to .attach() or .hash_grid().
1828
+
1829
+ Not every value can be set right now; at a later time
1830
+ that API may become more flexible. But for now this
1831
+ has to suffice.
1832
+
1833
+ ## Progress Bars in LibUI
1834
+
1835
+ Here an image how this may look:
1836
+
1837
+ <img src="https://i.imgur.com/i1i4ppZ.png" style="margin-left: 2em">
1838
+
1839
+ If you use the LibuiParadise project then you can create a new
1840
+ instance of a progress bar via <b>wrapper_new_progress_bar</b>.
1841
+
1842
+ Example:
1843
+
1844
+ progress_bar = wrapper_new_progress_bar
1845
+
1846
+ To then set a new value, use:
1847
+
1848
+ progress_bar.set_value()
1849
+ progress_bar.set_value(50) # will be 50%
1850
+
1851
+ You can then query this value via:
1852
+
1853
+ @progress_bar.value? # This will return a String, though.
1854
+
1855
+ ## Entries in LibUI (ui_entry)
1856
+
1857
+ The official source for an <b>ui_entry</b> for UNIX (including Linux systems)
1858
+ can be read here:
1859
+
1860
+ https://github.com/libui-ng/libui-ng/blob/master/unix/entry.c
1861
+
1862
+ Personally I tend to <b>create a new entry element</b> in this way:
1863
+
1864
+ entry = ui_entry
1865
+
1866
+ # or, if it shall be more GUI-agnostic, I do this:
1867
+
1868
+ entry1 = entry # where entry is entry() actually, and works for many GUI toolkits then
1869
+
1870
+ To <b>set new content</b> to such an entry, do make use
1871
+ of the following method call:
1872
+
1873
+ entry.set_text('foobar') # Make sure it is a String; that seems to work better.
1874
+
1875
+ To <b>query</b> the text content of such an entry, you can use the following API,
1876
+ if you use the libui_paradise gem:
1877
+
1878
+ entry.text?
1879
+
1880
+ To respond to events look at the **OnChanged** event.
1881
+
1882
+ To see a specific example where the user may input a password entry, have
1883
+ a look at the following file that is distributed within the libui_paradise
1884
+ gem:
1885
+
1886
+ libui_paradise/examples/complex/012_password_entry_example.rb
1887
+
1888
+ An entry can be <b>set read only</b> via:
1889
+
1890
+ entry.read_only # This has not been added into libui-paradise yet.
1891
+
1892
+ To <b>respond</b> to <b>on-changed events</b>, you can use:
1893
+
1894
+ LibUI.entry_on_changed()
1895
+
1896
+ A more complete example of this:
1897
+
1898
+ text_changed_callback = proc { |ptr|
1899
+ puts "Current textbox data: '#{UI.entry_text(ptr)}'"
1900
+ }
1901
+
1902
+ text_entry = ui_entry
1903
+ text_entry.set_text('Please enter a command')
1904
+ text_entry.on_changed {
1905
+ text_changed_callback
1906
+ }
1907
+
1908
+ The Proc object has to be passed into the {} block variation.
1909
+
1910
+ The latter uses:
1911
+
1912
+ LibUI.entry_on_changed(text_entry, text_changed_callback, nil)
1913
+
1914
+ If you use the libui_paradise gem, and only need to obtain the
1915
+ (changed) text of an entry in LibUI, then you can use the
1916
+ following simplified method call instead:
1917
+
1918
+ entry = ui_entry
1919
+ entry.on_changed {
1920
+ puts 'The text is now: '+entry.text?
1921
+ }
1922
+
1923
+ ADD_CONTACT_INFORMATION