libui_paradise 0.1.49

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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1505 -0
  3. data/doc/README.gen +1252 -0
  4. data/doc/SNIPPETS.md +221 -0
  5. data/doc/TODO.md +14 -0
  6. data/lib/libui_paradise/autoinclude.rb +9 -0
  7. data/lib/libui_paradise/domain_specific_language/README.md +5 -0
  8. data/lib/libui_paradise/domain_specific_language/button.yml +7 -0
  9. data/lib/libui_paradise/examples/001_basic_button_example.rb +17 -0
  10. data/lib/libui_paradise/examples/002_basic_table_image.rb +92 -0
  11. data/lib/libui_paradise/examples/003_font_button.rb +34 -0
  12. data/lib/libui_paradise/examples/004_date_time_picker.rb +31 -0
  13. data/lib/libui_paradise/examples/005_msg_box_error.rb +25 -0
  14. data/lib/libui_paradise/examples/006_histogram.rb +193 -0
  15. data/lib/libui_paradise/examples/007_combo_box_example.rb +41 -0
  16. data/lib/libui_paradise/examples/008_simple_notepad_example.rb +23 -0
  17. data/lib/libui_paradise/examples/009_checkbox_example.rb +82 -0
  18. data/lib/libui_paradise/examples/010_grid_example.rb +26 -0
  19. data/lib/libui_paradise/examples/011_fancy_text_example.rb +21 -0
  20. data/lib/libui_paradise/examples/012_control_gallery.rb +180 -0
  21. data/lib/libui_paradise/examples/013_midi_player.rb +91 -0
  22. data/lib/libui_paradise/examples/014_basic_draw_text.rb +134 -0
  23. data/lib/libui_paradise/examples/015_font_example.rb +91 -0
  24. data/lib/libui_paradise/examples/016_search_entry_example.rb +16 -0
  25. data/lib/libui_paradise/examples/017_tabs_example.rb +22 -0
  26. data/lib/libui_paradise/examples/018_image_example.rb +82 -0
  27. data/lib/libui_paradise/examples/019_open_file_button_example.rb +24 -0
  28. data/lib/libui_paradise/examples/020_unicode_text_example.rb +28 -0
  29. data/lib/libui_paradise/examples/021_spinbutton_example.rb +25 -0
  30. data/lib/libui_paradise/examples/022_text_example.rb +16 -0
  31. data/lib/libui_paradise/examples/023_parse_config_file_example.config +6 -0
  32. data/lib/libui_paradise/examples/023_parse_config_file_example.rb +13 -0
  33. data/lib/libui_paradise/examples/024_text_view_example.rb +14 -0
  34. data/lib/libui_paradise/examples/025_scrolling_area.rb +16 -0
  35. data/lib/libui_paradise/examples/026_colour_button.rb +51 -0
  36. data/lib/libui_paradise/examples/027_form_example.rb +37 -0
  37. data/lib/libui_paradise/examples/028_password_entry_example.rb +17 -0
  38. data/lib/libui_paradise/examples/029_two_buttons_showing_how_to_enable_and_disable_them.rb +32 -0
  39. data/lib/libui_paradise/examples/030_table_example.rb +51 -0
  40. data/lib/libui_paradise/experimental/dsl.rb +17 -0
  41. data/lib/libui_paradise/extensions/extensions.rb +25 -0
  42. data/lib/libui_paradise/extensions/hash_fiddle_pointer_widgets.rb +129 -0
  43. data/lib/libui_paradise/extensions/misc.rb +309 -0
  44. data/lib/libui_paradise/fiddle/pointer.rb +841 -0
  45. data/lib/libui_paradise/images/LIBUI_PARADISE_LOGO.png +0 -0
  46. data/lib/libui_paradise/images/README.md +2 -0
  47. data/lib/libui_paradise/images/form_example.png +0 -0
  48. data/lib/libui_paradise/libui_classes/area_handler.rb +52 -0
  49. data/lib/libui_paradise/libui_classes/button.rb +82 -0
  50. data/lib/libui_paradise/libui_classes/checkbox.rb +37 -0
  51. data/lib/libui_paradise/libui_classes/color_button.rb +44 -0
  52. data/lib/libui_paradise/libui_classes/combobox.rb +55 -0
  53. data/lib/libui_paradise/libui_classes/editable_combobox.rb +51 -0
  54. data/lib/libui_paradise/libui_classes/entry.rb +80 -0
  55. data/lib/libui_paradise/libui_classes/font.rb +78 -0
  56. data/lib/libui_paradise/libui_classes/font_button.rb +40 -0
  57. data/lib/libui_paradise/libui_classes/grid.rb +55 -0
  58. data/lib/libui_paradise/libui_classes/hbox.rb +98 -0
  59. data/lib/libui_paradise/libui_classes/horizontal_separator.rb +41 -0
  60. data/lib/libui_paradise/libui_classes/image.rb +82 -0
  61. data/lib/libui_paradise/libui_classes/label.rb +83 -0
  62. data/lib/libui_paradise/libui_classes/menu.rb +36 -0
  63. data/lib/libui_paradise/libui_classes/msg_box.rb +97 -0
  64. data/lib/libui_paradise/libui_classes/msg_box_error.rb +49 -0
  65. data/lib/libui_paradise/libui_classes/multiline_entry.rb +59 -0
  66. data/lib/libui_paradise/libui_classes/non_wrapping_multiline_entry.rb +41 -0
  67. data/lib/libui_paradise/libui_classes/open_file.rb +42 -0
  68. data/lib/libui_paradise/libui_classes/password_entry.rb +49 -0
  69. data/lib/libui_paradise/libui_classes/progressbar.rb +41 -0
  70. data/lib/libui_paradise/libui_classes/radio_buttons.rb +36 -0
  71. data/lib/libui_paradise/libui_classes/scrolling_area.rb +85 -0
  72. data/lib/libui_paradise/libui_classes/search_entry.rb +36 -0
  73. data/lib/libui_paradise/libui_classes/spinbox.rb +55 -0
  74. data/lib/libui_paradise/libui_classes/tab.rb +39 -0
  75. data/lib/libui_paradise/libui_classes/table.rb +45 -0
  76. data/lib/libui_paradise/libui_classes/text_layout.rb +42 -0
  77. data/lib/libui_paradise/libui_classes/vbox.rb +51 -0
  78. data/lib/libui_paradise/libui_classes/window.rb +167 -0
  79. data/lib/libui_paradise/project/project.rb +26 -0
  80. data/lib/libui_paradise/prototype/README.md +3 -0
  81. data/lib/libui_paradise/prototype/prototype.rb +101 -0
  82. data/lib/libui_paradise/requires/require_the_libui_classes.rb +26 -0
  83. data/lib/libui_paradise/requires/require_the_libui_paradise_project.rb +11 -0
  84. data/lib/libui_paradise/version/version.rb +17 -0
  85. data/lib/libui_paradise.rb +1 -0
  86. data/libui_paradise.gemspec +49 -0
  87. metadata +164 -0
data/README.md ADDED
@@ -0,0 +1,1505 @@
1
+ [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](https://www.gobolinux.org/)
2
+ [![forthebadge](http://forthebadge.com/images/badges/made-with-ruby.svg)](https://www.ruby-lang.org/en/)
3
+ [![Gem Version](https://badge.fury.io/rb/libui_paradise.svg)](https://badge.fury.io/rb/libui_paradise)
4
+
5
+ This gem was <b>last updated</b> on the <span style="color: darkblue; font-weight: bold">17.10.2021</span> (dd.mm.yyyy notation), at <span style="color: steelblue; font-weight: bold">05:33:59</span> o'clock.
6
+
7
+ ## The libui_paradise project
8
+
9
+ <img src="https://i.imgur.com/hYf3sum.png" style="margin: 1em; padding: 8px">
10
+
11
+ (This image has been partially auto-generated via **cfdg**, then modified
12
+ by me via **gimp** and ImageMagick - the rounded borders were
13
+ done via ImageMagick. You can re-use this image if you want to, including
14
+ the colour-pattern, via a **CC BY 3.0** licence. See the following link
15
+ for that licence: https://creativecommons.org/licenses/by/3.0/. For
16
+ cfdg itself, have a look at: https://www.contextfreeart.org/gallery/)
17
+
18
+ The **libui_paradise project** aims to enhance the official (upstream)
19
+ ruby-libui bindings a little bit.
20
+
21
+ You can find the upstream ruby-libui bindings, maintained by **kojix2**,
22
+ here:
23
+
24
+ https://rubygems.org/gems/libui
25
+
26
+ (Or visit the github page for ruby-libui here via this link:
27
+ https://github.com/kojix2/LibUI )
28
+
29
+ (Be wary of the name that I use on this page - the currently maintained gem is
30
+ called **libui**; the older gem, which was called **libui-ruby**, is no longer
31
+ maintained since as of **2019**. In the document here I may call kojix2' gem
32
+ **ruby-libui**, but the official name is simply **libui**, which refers to the
33
+ gem mentioned above: https://rubygems.org/gems/libui - I understand that I
34
+ am using a misnomer when I refer to this as **ruby-libui**, but it helps
35
+ me remember, so ...)
36
+
37
+ Aside from this mentioned goal of **trying to enhance** the upstream ruby-bindings,
38
+ the **libui_paradise** project also attempts to demonstrate how we could **try to
39
+ use a DSL** to write less code in the long run; at the least less syntax.
40
+
41
+ Less syntax is not automatically a win-win situation, but often it can
42
+ be very useful.
43
+
44
+ Example for this:
45
+
46
+ _ = button('Hello world!') # for libui
47
+
48
+ You can use a similar variant in ruby-gtk3, for example:
49
+
50
+ _ = Gtk::Button.new('Hello world!')
51
+
52
+ Or, if you want to use "toplevel-methods", like the above, identical to the
53
+ first variant:
54
+
55
+ _ = button('Hello world!') # for ruby-gtk
56
+
57
+ In fact: if you notice the above three lines of code then there is indeed no
58
+ difference between which toolkit to use between the first variant and
59
+ the last variant. We could then, in theory, "plug in" different toolkits,
60
+ be it ruby-gtk, ruby-tk, ruby-libui and so forth. The **glimmer** project
61
+ is doing this to some extent, as a general DSL wrapper over GUI-related
62
+ functionality, including the www - see here: https://github.com/AndyObtiva/glimmer
63
+
64
+ We could even extend this to the www and generate the appropriate
65
+ tags that way, by treating HTML tags as "objects". Note that this is
66
+ "in theory"; in practice there are some shortcomings, or limitations,
67
+ such as different toolkits not supporting the same widgets or
68
+ functionality. The old ruby-qt toolkit, for example, required you to
69
+ connect slots to signals, due to qt requiring this. I do not know if
70
+ this is still the case or not, but back in the days this was
71
+ necessary and it complicated using ruby-qt a little bit.
72
+
73
+ The **libui_paradise gem** here is highly experimental at this stage
74
+ and may not work for all use cases, or may have bugs - I am still
75
+ learning myself here. I want to see which API calls make the most sense
76
+ in the long run, ideally even across different GUIs as well as the
77
+ www. (For those of you who have some experience with ruby-gtk this may
78
+ seem familiar, as pointed out above - API elements such as **.set_text()**
79
+ or **widget1.add(widget2)**; I kind of learned GUIs first via
80
+ **ruby-gtk**, which evidently shaped my opinion on GUI layouts
81
+ to some extent.)
82
+
83
+ The official ruby-libui project maintained by kojix2 comes with
84
+ **13 examples** (August 2021) so far. I assume that more examples
85
+ may be added over the coming months and years depending on use
86
+ case, time availability, motivation and so forth. Have a look at
87
+ the code provided on the github page to understand what is going
88
+ on - in particular the **histogram** example is really nifty if
89
+ you want to play around with it **interactively**. It is probably the
90
+ best example in this regard, because it features interactive and
91
+ dynamic use in a visual way - you can choose the colours, for
92
+ example. A spin-button allows for visual change too, but I think
93
+ colours are more impressive than simple up-and-down counters.
94
+
95
+ On windows this may look like so (via kotlin-libui):
96
+
97
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/histogram/histogram-windows7.png" style="margin-left: 2em">
98
+
99
+ Also check out kojix2' other examples in ruby-gtk - would be great
100
+ if we could have the same in libui one day, but this also depends
101
+ on what upstream libui makes available, unless it is somehow
102
+ possible to connect multiple shared libraries into libui; then
103
+ we could perhaps extend libui.
104
+
105
+ I am trying to think about ways to simplify the code in these
106
+ examples as well, so that we can "do more by writing less". But as
107
+ stated before: this is all **highly experimental** and subject
108
+ to change. I am going via slow, tiny babysteps here! Constant
109
+ wins the race in the long run, just remember the turtle versus
110
+ the rabbit. The rabbit always believed that he would win the
111
+ race ...
112
+
113
+ **kojix2** pointed out that a half-baked or incomplete OOP design may
114
+ not make a lot of sense in regards to ruby-libui - we have to think a
115
+ bit about **Fiddle::Pointer** which not everyone may have done before.
116
+
117
+ Otherwise you may end up segfaulting everything all over the place
118
+ (which I did run into already - but it is both scary and fun at the
119
+ same time!). Subclassing is also difficult to do properly - which
120
+ functions should be called to create a "proper" subclass? May there
121
+ be problems as a consequence of trying this? I have no idea right
122
+ now. This is definitely more related to the C API of ruby than
123
+ "pure", plain ruby as such. You kind of have to understand the
124
+ underlying C code to some extent.
125
+
126
+ I do not really know C very well; **pointers** are way above my
127
+ level of understanding, so **kojix2's** point is a fair one
128
+ to make, since someone may have to maintain a growing code base -
129
+ this may well be the case here too, so 'defensive programming'
130
+ is a viable strategy. There are examples where one has to work
131
+ around ruby's GC too, for instance - this all makes this a little
132
+ bit more complicated than plain, "pure" ruby code.
133
+
134
+ **However had**, at the same I still want to **experiment** and see
135
+ what may happen with the code in general - which pitfalls may happen
136
+ or which things could be improved upon. I think the more people use
137
+ libui and ruby-libui, the better this may become in the long run.
138
+ Imagine we could almost create a full desktop system based on
139
+ libui! Even if the original scope never was aimed towards that. :D
140
+
141
+ This may then be fast enough for ruby-on-the-desktop, and simple
142
+ enough to do that, too. Right now this has too many shortcomings;
143
+ I miss CSS styling in particular. But, in theory, we could do
144
+ this. Anyone feels clever enough to use ruby as a desktop-UI
145
+ language via libui? I suppose it still requires some decent
146
+ knowledge of C ...
147
+
148
+ Lots of different people could create widgets and add-ons when
149
+ this were possible ... but I digress.
150
+
151
+ **Write a GUI once, run everywhere** (well ... at the least in
152
+ theory). This is an excellent idea, even if libui may not be
153
+ around one day, that idea should be retained for other GUIs
154
+ in the future.
155
+
156
+ It's quite difficult to get GTK and ruby-gtk to work on
157
+ **windows** - I tried to compile it some weeks ago but I
158
+ ended up having "missing symbols" error messages afterwards.
159
+ I managed to get the hello-world.c example working, but the
160
+ more complicated examples did not work for me.
161
+
162
+ On Linux this is much, much easier to do ... I literally just
163
+ compile GTK, after its dependencies are properly installed (glib,
164
+ pango, atk, and so forth), and then the ruby bindings maintained
165
+ largely by kou (and others), and ... it works! At the least
166
+ on linux.
167
+
168
+ (I used to be able to run ruby-gtk2 on windows in the past,
169
+ a long time ago, using the provided binaries, but sadly upstream
170
+ GTK developers no longer provide binaries as-is, and there are
171
+ no binary bundles for ruby-gtk on windows anymore either. I
172
+ assume it is possible if you know msys2, and the windows
173
+ platform, but I am no expert on either, so ...)
174
+
175
+ **libui** is so much simpler to use on windows than GTK,
176
+ though - just do **gem install libui** and it'll work,
177
+ as-is. Literally. That's it. I tried it on my windows
178
+ laptop and it **does** indeed work. That convinced me
179
+ that it makes sense to use libui and ruby-libui. The reason it
180
+ works is because the ruby-libui gem (just called **libui**)
181
+ bundles the respective binaries, and it is quite small. This
182
+ would be much harder to do with ruby-gtk.
183
+
184
+ Now I am trying to find more awesome examples to showcase
185
+ what can be done. Who knows - perhaps even CSS may be supported
186
+ one day (perhaps only on linux though, which limits the
187
+ benefit of it; ruby-gtk3 does allow for CSS though). A
188
+ font example has also been provided, so we can upscale,
189
+ downscale, use different fonts, bold, in colours and so
190
+ on and so forth now.
191
+
192
+ Check out the colours in the file **basic_draw_text.rb**,
193
+ as courtesy provided by kojix2. That way you can style
194
+ the content of a widget more easily - only the fiddle-protection
195
+ against segfault parts is a bit strange, but that's a detail.
196
+
197
+ There currently is quite a bit of code to make this work;
198
+ in the long run I plan to simplify this if possible. **The
199
+ less code we have to write, the better** - as long as the
200
+ end code is still **readable**.
201
+
202
+ Note that the subclass situation may change eventually; see
203
+ upstream kojix2 and related discussion for fiddle. For
204
+ the time being, though, I'll retain my old approach until
205
+ I am sufficiently content with the project as-is. Right now
206
+ libui_paradise has way too much undocumented and untested
207
+ code and I still have not added all examples either (coloured
208
+ text, for instance, still has to be added; and more image-related
209
+ examples that are smaller). Stay tuned.
210
+
211
+ ## How to require the libui_paradise project
212
+
213
+ In order to **require the libui_paradise project**, use
214
+ something like the following:
215
+
216
+ require 'libui_paradise'
217
+
218
+ More frequently if you look at the **examples/** subdirectory, the
219
+ following is used instead:
220
+
221
+ require 'libui_paradise/autoinclude'
222
+
223
+ Note that this particular require call does a few things automatically,
224
+ which may not always be what you may want to use. So, again - check
225
+ out the official ruby-libui repository first, including the examples,
226
+ before having a look at **libui_paradise**. This project here is a
227
+ tinker-project, very unstable, subject to change - don't use it in
228
+ production yet.
229
+
230
+ In my own projects I tend to use the above autoinclude variant
231
+ most of the time, because that way I can write less code (omit
232
+ a few lines).
233
+
234
+ ## How to get started with ruby-libui and the libui_paradise project?
235
+
236
+ Well - as stated elsewhere, I first recommend to you that you look directly
237
+ at ruby-libui provided by kojix2, in particular the examples that he
238
+ distributes in the gem. Work through the examples step-by-step, possibly
239
+ starting with the smallest example, see whether they work (they do,
240
+ or should) and have a look at how things work - just to get an
241
+ overview.
242
+
243
+ Have a look at the code as well, in order to understand how it
244
+ works roughly; after some minutes or perhaps a very few hours of tinker-time
245
+ you should understand quite a bit already, if you already know ruby. Even
246
+ more so if you did work with GUIs before, including GUIs via a www-interface.
247
+
248
+ Then you are recommended to look at the libui_paradise project and look to
249
+ see what has been added on top of what kojix2 provides. Look at the examples
250
+ of the libui_paradise project as well (they are a bit simplified compared
251
+ to the upstream examples; unfortunately a few of them currently do not
252
+ work), then consider using **libui_paradise/prototype/prototype.rb** in
253
+ particular. Copy it and adjust it to your project and use case, as-is. You
254
+ may want to remove the grid that is inside there and use a hbox or a vbox
255
+ instead, or a padded_hbox and padded_vbox. It's all quite simple actually
256
+ once you understand the basic API: windows, widgets, buttons, entries.
257
+
258
+ For example, a button can be added in this way to a vbox, if you use
259
+ the libui_paradise gem:
260
+
261
+ outer_vbox = padded_vbox # padded means that it will have some padding to the sides
262
+ button = ui_button('Hello world!') # You can drop the ui_ prefix if you'd like to
263
+ button.on_clicked {
264
+ puts 'Hello world!'
265
+ }
266
+ outer_vbox.minimal(button) # Use minimal space if possible. Or use .add().
267
+
268
+ That's it! Inside of the **.on_clicked {}** block you can run the ruby code
269
+ that you want to use. A button that is in a container (such as **vbox**) that
270
+ outputs hello world when clicked. Can't get much simpler than that. \o/
271
+
272
+ You can omit the **ui_** part above. I just use it to point out the difference;
273
+ and because I'd otherwise may have to use "button = button" aka "button = button()"
274
+ which may be confusing, so I use **ui_button()** instead.
275
+
276
+ ## Fiddle::Pointer and playing with pointers
277
+
278
+ The **ruby-libui bindings** make use of **Fiddle::Pointer** a lot -
279
+ see the file called **ffi.rb** in the ruby-libui gem ("gem install libui").
280
+ It's like **magic** to me - scary and awesome at the same time.
281
+
282
+ As a consequence the libui_paradise project has to 'handle' pointers
283
+ as well, indirectly so, via whatever ruby-libui makes available.
284
+
285
+ I decided that, at the least for the time being, we will add
286
+ **ad-hoc code** straight to Fiddle::Pointer. This is not the
287
+ optimal solution and I do not recommend doing so, even more so as
288
+ we modify Fiddle::Pointer directly which I don't think is a good
289
+ idea; it may be better to have some proper data structures and
290
+ perhaps **subclass** from Fiddle::Pointer instead, and then modify
291
+ that subclass instead. That may be better. But for the time being,
292
+ the code here will remain as it is, until we can come up with
293
+ better ways to deal with the situation. For now **simplicity**
294
+ rules.
295
+
296
+ Some of the examples used require assigment to local variables
297
+ to avoid the GC to kick in and cause the program to end. This
298
+ is presently not very elegant - see a discussion between kojix2
299
+ and kou to improve on this part. For the time being, though,
300
+ some of the examples require 'boilerplate' assignment to
301
+ variabless. Stay tuned for improvements in this regard; ideally
302
+ we can use libui without having to play with pointers ever.
303
+
304
+ ## Constraints of the libui_paradise gem
305
+
306
+ The libui_paradise gem comes with some **constraints**.
307
+
308
+ For example, many of the ad-hoc methods on **Fiddle::Pointer**
309
+ will only work after you called e. g. **ui_vbox** or a
310
+ similar constructor where we registered which widgets are
311
+ created (aka the **new_** methods that are available on
312
+ the **LibUI** 'namespace'). Only when this has happened
313
+ will that widget become registered in the main Hash.
314
+
315
+ That then means, logically, that if you use a method such as:
316
+
317
+ check_button = ui_check_button
318
+ check_button.is_active?
319
+
320
+ This will only work if that widget was created already prior
321
+ to that "method call".
322
+
323
+ What this means in practice is that it is best if you
324
+ **create all the skeleton** (the **basic UI elements**) **before**
325
+ calling any other code, including on-clicked or on-toggle
326
+ events. Otherwise you may find nil values and don't
327
+ know why that is the case so. If this seems too complicated
328
+ for you, don't worry - the examples distributed via the
329
+ libui_paradise gem should work fine (except two buggy ones), so
330
+ the point of this subsection here is to keep your attention to
331
+ the fact that, as of right now, if you use libui_paradise, the
332
+ **proper-order-of-elements is important**. Ideally create all
333
+ the widgets first, before you add additional functionality to them.
334
+
335
+ This is admittedly not a very elegant limitation right
336
+ now, and one day this restrictions may be removed or
337
+ lifted - but for now, it is a limitation that requires
338
+ a tiny bit of discipline to work around, for the time being.
339
+ So, again - it is best to split up the skeleton UI from
340
+ the function.
341
+
342
+ ## How to use an 'Open File' button
343
+
344
+ I came up with the following solution for now:
345
+
346
+ outer_vbox = padded_vbox
347
+ button_open_file = button('Open file')
348
+ button_open_file.on_clicked {
349
+ filename = open_file(window).to_s # This is the part that will open a local file.
350
+ }
351
+ outer_vbox << button_open_file # Add the button to the outer-vbox.
352
+
353
+ Calling the **.to_s** method on the Fiddle::Pointer yields the actual
354
+ filename. You can then add more code to deal with this.
355
+
356
+ **window** above refers to the **main_window**. See the example
357
+ **019_open_file_button_example.rb** for how this actually works.
358
+
359
+ In the future this may be even further simplified a little, as
360
+ opening local files is a very common task in most GUIs. A
361
+ single method probably should suffice for using such a
362
+ specialized button.
363
+
364
+ ## How to quit from a ruby-libui widget
365
+
366
+ I found that:
367
+
368
+ UI.quit
369
+
370
+ Works best overall. And seems to suffice as well.
371
+
372
+ There are some other calls, such as destroy-control and similar
373
+ actions, which are probably clean-up related - but by and large
374
+ **UI.quit** seems to be the main part how to exit from a
375
+ libui-application.
376
+
377
+ I document this here in the event that I forget it in a few
378
+ months.
379
+
380
+ If you use the libui_paradise gem then you can use the
381
+ following method to use a quit button:
382
+
383
+ a_quit_button = ui_quit_button
384
+
385
+ If you want another textual description then you can do the
386
+ following:
387
+
388
+ a_quit_button = ui_quit_button(text: 'Open a local file') # That text description would be confusing though
389
+ a_quit_button = ui_quit_button(text: 'Exit the application') # Much better now! \o/
390
+
391
+ ## UI.new_button()
392
+
393
+ UI.new_button allows us to create a new button.
394
+
395
+ Examples:
396
+
397
+ button1 = UI.new_button('Text')
398
+ button2 = UI.new_button('▶')
399
+
400
+ ## Subclassing
401
+
402
+ Currently subclassing from LibUI elements does not work - I simply
403
+ have no idea how to "subclass" from a **Fiddle::Pointer**. Perhaps we
404
+ have to build up a data structure that behaves like Fiddle::Pointer
405
+ but also has methods that allow 'OOP behaviour'. Has anyone tried
406
+ this yet? I am scared to try considering I already got segfaults ...
407
+
408
+ Eventually I may figure this out - or someone else with more knowledge
409
+ will make this available. We can probably 'fake' subclassing to
410
+ a pointer somehow ... after all ruby internally has to figure it
411
+ out as well and probably did so already, on the C-level side.
412
+
413
+ ## Quickly adding text to a widget
414
+
415
+ You can use something like this:
416
+
417
+ outer_vbox = ui_vbox
418
+ outer_vbox.text(
419
+ 'This widget can be used to do xyz.'
420
+ )
421
+
422
+ Again - this modifies **Fiddle::Pointer** so be wary. The second
423
+ argument is the default one for use in a label / ui_text widget.
424
+
425
+ ## Working with combo-boxes
426
+
427
+ To create a combo-box, do:
428
+
429
+ combo_box = ui_combo_box
430
+
431
+ You can fill it with an array of entries **on creation-time** via:
432
+
433
+ combo_box = ui_combo_box([1,2,3,4])
434
+
435
+ This will also focus on the very first element when doing so.
436
+
437
+ If you need to do so manually, and focus on another element,
438
+ you can use the following toplevel method:
439
+
440
+ UI.combobox_set_selected()
441
+
442
+ To query the currently selected value, use:
443
+
444
+ UI.combobox_selected(pointer)
445
+
446
+ This is usually done via a **proc {}** object. See kojix2' examples.
447
+
448
+ In LibuiParadise a few custom methods were added, such as
449
+ **.ui_sync_connect()**. This method was added to connect a
450
+ combo-box to a entry and automatically sync that entry whenever
451
+ the combo box is changed. See the example **007_combo_box_example.rb**
452
+ for how this works.
453
+
454
+ The **source code** to the combo-box in libui, at the least
455
+ for UNIX/Linux, can be seen here:
456
+
457
+ https://github.com/andlabs/libui/blob/master/unix/combobox.c
458
+
459
+ ## Error messages and ui_error_message
460
+
461
+ In LibUI respectively ruby-libui you can display error messages
462
+ via a popup window.
463
+
464
+ The API is something like this:
465
+
466
+ UI.msg_box(main_window, 'Information', 'You clicked the button')
467
+ UI.msg_box_error(main_window, 'Information', 'You clicked the button')
468
+
469
+ The first line shows a normal message box; the second line shows how to
470
+ use a message box specifically 'adapted' for displaying errors to the
471
+ end user.
472
+
473
+ LibuiParadise makes this available via **ui_msg_box** and **ui_msg_box_error**
474
+ respectively.
475
+
476
+ ## Libui Form
477
+
478
+ **Form** is a container that takes labels for its contents. This is currently
479
+ just a stub though - we may have to research this with better examples.
480
+
481
+ ## Libui Checkbox
482
+
483
+ A simple checkbox example in **plain** ruby-libui follows:
484
+
485
+ checkbox = UI.checkbox('Checkbox')
486
+ checkbox_toggle_callback = proc { |ptr|
487
+ checked = UI.checkbox_checked(ptr) == 1
488
+ UI.checkbox_set_text(ptr, "I am the checkbox (#{checked})")
489
+ }
490
+
491
+ This may look like so on Linux:
492
+
493
+ <img src="https://i.imgur.com/d7qWalZ.png" style="margin-left: 2em; padding: 4px; border: 1px solid black;">
494
+
495
+ To query whether a checkbox is **active**, use code such as the
496
+ following:
497
+
498
+ checkbox.is_active?
499
+ checkbox.active?
500
+
501
+ This depends on the modifications to Fiddler::Pointer, so
502
+ be wary when you use this - there be dragons (perhaps). Most
503
+ of these modifications are based on **.object_id**, which is
504
+ registered in a main, toplevel Hash in the
505
+ **libui_paradise project**. Not very elegant, but simple, and
506
+ it works (for the most part).
507
+
508
+ ## Adding a widget into another widget
509
+
510
+ I chose the following **API** for this:
511
+
512
+ box1.add(box2, 1)
513
+
514
+ Note that this is "cheating" a bit because the method **.add()** is defined
515
+ on **Fiddle::Pointer**. That's scary! Segfaults coming your way. But it
516
+ also seems to work to some extent. Which is amazing ... :-)
517
+
518
+ In ruby-gtk it is quite common to use **.add()**. While **.pack_start()**
519
+ and **.pack_end()** are available in ruby-gtk as well, I think .add() is
520
+ the simpler name. We just **add a widget to another widget** - job done.
521
+
522
+ (I may also use << as alias to .add() and while << is great, remember
523
+ that it can not easily be used all the time, e. g. box1 << box2 <<
524
+ box3 versus box1.add(box2).add(box3) - the latter is a bit more
525
+ resilient syntax-wise.)
526
+
527
+ As stated, **<<** was added as an alias to **.add()** but I am not yet sure
528
+ if this is a very good idea. It's super-nifty to use << everywhere, but it
529
+ also changes the syntax of the whole .rb file ... on the other hand, using
530
+ << is easier than .add() so ... I'll go with that as well. Remember
531
+ there is more than one way to do something in ruby - you need to
532
+ select the variant(s) that work best for you and possibly ignore the
533
+ other variants.
534
+
535
+ Since a while the above can be simplified a bit, as will be shown
536
+ next.
537
+
538
+ Rather than use:
539
+
540
+ box1.add(box2, 1)
541
+
542
+ You can now do this instead:
543
+
544
+ box1.maximal(box2)
545
+
546
+ This is a tiny bit longer, but you can omit the ", 1" part, which is
547
+ nice. The alternative is .minimal(), which defaults to:
548
+
549
+ add(second_widget, 0)
550
+
551
+ So the only difference between .maximal() and .minimal() will be
552
+ whether you pass 0 or 1 to the method. See the upstream libui
553
+ API to understand the difference.
554
+
555
+ ## Libui Tabs
556
+
557
+ The notebook-tab may look like this:
558
+
559
+ <img src="https://i.imgur.com/olWQAIJ.png" style="margin-left: 2em">
560
+
561
+ ## Create a vertical box:
562
+
563
+ Use code like this:
564
+
565
+ vbox = UI.new_vertical_box
566
+
567
+ If you use the libui_paradise gem, you can use this:
568
+
569
+ vbox = ui_vbox # or
570
+ vbox = padded_vbox
571
+
572
+ ## Adding a horizontal separator
573
+
574
+ The method **UI.new_horizontal_separator** can be used to add (or rather
575
+ first create) a horizontal separator.
576
+
577
+ You can then add it via .add() or << if you use the libui_paradise
578
+ project. Alternatively you can use the toplevel method provided by
579
+ ruby-libui, since that is what the libui_paradise project is doing anyway.
580
+
581
+ To simplify this further, you can do something like this:
582
+
583
+ outer_vbox = ui_vbox # I call the most-outer vbox usually outer-vbox
584
+ outer_vbox.add_hsep
585
+ # outer_vbox.add_horizontal_separator # Or this variant if you prefer some verbosity instead.
586
+
587
+ Or, perhaps better, use a padded vbox:
588
+
589
+ outer_vbox = padded_vbox # note that "ui_" is not used here
590
+ outer_vbox.add_hsep
591
+
592
+ I needed this functionality to quickly add horizontal separators for some
593
+ visual cue in the **User Interface**. Using **.add_hsep** is very
594
+ convenient and fast to write/type. If you want more verbosity then remember
595
+ that you can always use the upstream API as-is.
596
+
597
+ ## Entries in LibUI (ui_entry)
598
+
599
+ The official source for an ui_entry for Unix (including Linux) can be read
600
+ here:
601
+
602
+ https://github.com/andlabs/libui/blob/master/unix/entry.c
603
+
604
+ Personally I tend to create a new entry element in this way:
605
+
606
+ entry = ui_entry
607
+
608
+ To set new content to it, do:
609
+
610
+ entry.set_text('foobar') # Make sure it is a String; that seems to work better.
611
+
612
+ To respond to events look at **OnChanged**.
613
+
614
+ It can be set read only via:
615
+
616
+ entry.read_only # This has not been added into libui-paradise yet.
617
+
618
+ To respond to on-changed events, you can use:
619
+
620
+ UI.entry_on_changed()
621
+
622
+ Complete example:
623
+
624
+ text_changed_callback = proc { |ptr|
625
+ puts "Current textbox data: '#{UI.entry_text(ptr)}'"
626
+ }
627
+
628
+ text_entry = ui_entry
629
+ text_entry.set_text('Please enter a command')
630
+ text_entry.on_changed {
631
+ text_changed_callback
632
+ }
633
+
634
+ The Proc object has to be passed into the {} block variation.
635
+
636
+ The latter uses:
637
+
638
+ UI.entry_on_changed(text_entry, text_changed_callback, nil)
639
+
640
+ ## Grids in LibUI
641
+
642
+ The API is quite complex and hard to remember:
643
+
644
+ UI.grid_append(grid, entry1, 0, 0, 2, 1, 0, 0, 1, 0)
645
+
646
+ Who can remember offhand what these values mean?
647
+
648
+ The upstream struct is this:
649
+
650
+ struct gridChild {
651
+ uiControl *c;
652
+ int left;
653
+ int top;
654
+ int xspan;
655
+ int yspan;
656
+ int hexpand;
657
+ uiAlign halign;
658
+ int vexpand;
659
+ uiAlign valign;
660
+
661
+ // have these here so they don't need to be reallocated each relayout
662
+ int finalx, finaly;
663
+ int finalwidth, finalheight;
664
+ int minwidth, minheight;
665
+ };
666
+
667
+ The documentation for Go has this signature:
668
+
669
+ func (g *Grid) Append(child Control, left, top int, xspan, yspan int, hexpand bool, halign Align, vexpand bool, valign Align)
670
+
671
+ Append adds the given control to the Grid, at the given coordinate.
672
+
673
+ I assume that **uiControl c** refers to the widget that is to be embedded
674
+ into the grid, so the numbers that follow afterwards are the ones
675
+ that are important. Let's have a look at them, based on the above API
676
+ call, and only list these again, without the **()**:
677
+
678
+ # left, top, xspan, yspan, hexpand, halign, vexpand, valign
679
+ # 0, 0, 2, 1, 0, 0, 1, 0
680
+
681
+ xspan and yspan refer to width; left and top refer to the position
682
+ in the grid. hexpand and vexpand means whether the grid-cell will
683
+ expand horizontally or vertically. halign and valign, I think,
684
+ are used to align the grid-cell horizontally and vertically, so you
685
+ can position them exactly in the middle.
686
+
687
+ Recently (August 2021) I discovered that you can actually put
688
+ a button-in-a-button. I don't know whether this is a bug or
689
+ a feature, but it is hilarious.
690
+
691
+ The 'raw' code I used for this was the following:
692
+
693
+ UI.grid_append(grid, UI.new_button('3'),0,1,1,1,1,1,1,1)
694
+ UI.grid_append(grid, UI.new_button('4'),1,1,1,1,1,1,1,1)
695
+ UI.grid_append(grid, UI.new_button('5'),0,1,1,1,1,0,1,0)
696
+ UI.grid_append(grid, UI.new_button('6'),1,1,1,1,1,0,1,0)
697
+
698
+ This led to the following image:
699
+
700
+ <img src="https://i.imgur.com/6sWwWKh.png" style="margin-left: 1em">
701
+
702
+ The smaller buttons and the larger buttons can be clicked. They
703
+ reside in the same grid-cell. I don't know whether this is a
704
+ bug or a feature really, but this was quite hilarious to see.
705
+ In the event that I may forget this, I'll keep this here as
706
+ a reference.
707
+
708
+ ## Margins in LibUI
709
+
710
+ The **margin** property specifies if the window content should have a
711
+ margin or not. The default value is false, meaning that there will
712
+ be no margin.
713
+
714
+ In order to understand the difference, a visual image may be
715
+ best - the first image that is shown next shows **no margin**,
716
+ whereas the second image **shows** a margin.
717
+
718
+ <img src="https://cloud.githubusercontent.com/assets/11197111/16465935/804a30d4-3e41-11e6-8189-150e8cddc152.png" style="margin-left: 2em"><br>
719
+ <img src="https://cloud.githubusercontent.com/assets/11197111/16465906/68304cfe-3e41-11e6-8ae0-3123205ee136.png" style="margin-left: 2em"><br>
720
+
721
+ Note that the API name is **margined** rather than **margin** -
722
+ got me a little while to get used to that name.
723
+
724
+ The API in Go is: __func (*Group) SetMargined__ respectively
725
+ __func (g *Group) SetMargined(margined bool)__. When **true**
726
+ then the group has margins around the child widgets, as mentioned
727
+ already.
728
+
729
+ The **size** of the margins in use, is, unfortunately, determined
730
+ automatically by the OS - we currently (2021) do not have control
731
+ over the size of the margin at hand via **libui**.
732
+
733
+ Code examples for 'raw' **ruby-libui** are these:
734
+
735
+ UI.window_set_margined(MAIN_WINDOW, 1)
736
+ UI.group_set_margined(group, 1)
737
+
738
+ Because I prefer calling methods on objects, I added this instead:
739
+
740
+ MAIN_WINDOW.is_margined
741
+ MAIN_WINDOW.uses_a_margin # Or this variant.
742
+
743
+ Note that we are here faking method-calls on a Fiddle::Pointer, but
744
+ if we don't pay attention then it looks like **OOP**! If it walks
745
+ like a duck, quacks like a duck then ... it may be a
746
+ **Fiddle::Pointer:Duck**!!!
747
+
748
+ ## Entry
749
+
750
+ An entry can be set read only (readOnly: Boolean, aka true or false).
751
+
752
+ An entry in libui may look like this:
753
+
754
+ <img src="https://raw.githubusercontent.com/parro-it/libui-node/master/docs/media/UiEntry.png" style="margin-left:1em">
755
+
756
+ ## Borderless windows
757
+
758
+ A window that is **borderless: true** will not show any title or
759
+ outside frame. This may be useful for games and what not.
760
+
761
+ ## Spinbutton / Spinbox
762
+
763
+ You can use:
764
+
765
+ UI.new_spinbox
766
+
767
+ To create a new spinbox.
768
+
769
+ To specify the min and max range, pass them as parameters:
770
+
771
+ UI.new_spinbox(0, 100)
772
+
773
+ If you use the extensions then you can do this instead:
774
+
775
+ ui_spinbox
776
+ spinbox # this is the simplest variant
777
+
778
+ You should be able to set a value via set_value but I have
779
+ not yet added an example for this.
780
+
781
+ Relevant methods are:
782
+
783
+ UI.spinbox_on_changed()
784
+ UI.spinbox_set_value()
785
+ UI.spinbox_value()
786
+
787
+ To set a value:
788
+
789
+ spinbox.set_value(42)
790
+ spinbox.value = 42 # this works as well
791
+
792
+ ## Create a text-view widget
793
+
794
+ A text-view widget shows content, such as the content of a local file.
795
+
796
+ In libui the general API for this is:
797
+
798
+ UI.new_multiline_entry # this is a textview
799
+
800
+ ## Control Gallery
801
+
802
+ Here is an image, from kotlin-libui, how this may look on windows:
803
+
804
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/controlgallery/controlgallery-windows7.png" style="margin-left: 2em">
805
+
806
+ ## Enabling / Disabling buttons in libui
807
+
808
+ This is, assumingly, already possible via:
809
+
810
+ libui/ui.h
811
+
812
+ Lines 105 to 107 in a0a9807
813
+ _UI_EXTERN int uiControlEnabled(uiControl *);
814
+ _UI_EXTERN void uiControlEnable(uiControl *);
815
+ _UI_EXTERN void uiControlDisable(uiControl *);
816
+
817
+ In ruby-libui this should be possible via:
818
+
819
+ LibUI.control_enable()
820
+ UI.control_enable()
821
+
822
+ LibUI.control_disable()
823
+ UI.control_disable()
824
+
825
+ See the example **029_two_buttons_showing_how_to_enable_and_disable_them.rb**
826
+ in how this works.
827
+
828
+ ## libui and ruby-libui internals
829
+
830
+ This is an incomplete subsection. I know almost nothing at all about
831
+ C; kojix2 knows more, so I refer you to the homepage of ruby-libui
832
+ respectively.
833
+
834
+ Most of the code for ruby-libui resides under **ffi.rb**. In August 2021
835
+ this file contains almost 1000 lines, including newlines. Still quite
836
+ some code. If you want to look at the raw content, have a look at
837
+ the following link for **ffi.rb**:
838
+
839
+ https://raw.githubusercontent.com/kojix2/LibUI/main/lib/libui/ffi.rb
840
+
841
+ The two most important components there, as far as I understand it,
842
+ are **try_extern** and **structs**.
843
+
844
+ For instance:
845
+
846
+ try_extern 'void uiOnShouldQuit(int (*f)(void *data), void *data)'
847
+
848
+ Control = struct [
849
+
850
+ I assume that any support made available for ruby **must** have a
851
+ corresponding entry in **ffi.rb**. At the least this is how I
852
+ understood it.
853
+
854
+ If it is not in **ffi.rb** then support for that was not (yet) added.
855
+
856
+ Individual enum entries can also be seen. For instance, for font-related
857
+ data the following attributes are in use:
858
+
859
+ AttributeTypeFamily = 0
860
+ AttributeTypeSize = 1
861
+ AttributeTypeWeight = 2
862
+ AttributeTypeItalic = 3
863
+ AttributeTypeStretch = 4
864
+ AttributeTypeColor = 5
865
+ AttributeTypeBackground = 6
866
+ AttributeTypeUnderline = 7
867
+ AttributeTypeUnderlineColor = 8
868
+ AttributeTypeFeatures = 9
869
+
870
+ You can probably use the rubygems features of comparing old gem
871
+ releases with one another to see which support has been added to
872
+ ruby-libui over the past months or so. Most work was probably done
873
+ in 2020 or even before that (there are some older ruby bindings
874
+ to libui, but these work differently as kojix2 explained).
875
+
876
+ ## Limitations of/in LibUI
877
+
878
+ LibUI is not perfect - it is missing many things that should work just fine
879
+ on the main operating systems. On top of that I'd love to support specific
880
+ features on a given operating system even if it is NOT cross-platform. But
881
+ this is not possible via LibUI.
882
+
883
+ The following subsection mentions a few constraints of LibUI in varying
884
+ degrees of complexity/importance:
885
+
886
+ - No CSS support. I miss this. On ruby-gtk3 this is possible. In LibUI I can't.
887
+ - Unable to set different fonts for an application other than using FontButton.
888
+
889
+ ## Structure of the project
890
+
891
+ In **September 2021** the libui_paradise project was re-arranged slightly. There
892
+ is now a dedicated **libui_classes/** directory that "simulates" separate classes.
893
+ For example, libui_button now resides in **button.rb**. I use the same directory
894
+ layout in the **gtk_paradise** gem and I think it makes a lot of sense - at the
895
+ least I can quickly find the code that I may want to modify or extend. If I
896
+ want to extend buttons, then I modify **buttons.rb**. That's simple, right?
897
+
898
+ Another reason why the project was re-structured was so that we can use a
899
+ **unified DSL** for the GUI elements in the long run.
900
+
901
+ For example, code such as the following:
902
+
903
+ _ = button
904
+ _.on_clicked {
905
+ puts 'Hello world!'
906
+ }
907
+
908
+ Should work on ruby-gtk3, libui and all the other toolkits one day - even on
909
+ the www. But the different toolkits do not implement the same functionality.
910
+ Libui only offers a reduced functionality, for instance. Thus, the DSL that
911
+ we may then use **can only support a subset of functionality**, whereas other
912
+ toolkits are more useful in this regard. This was an additional reason why files
913
+ such as button.rb were created - it makes it easier to know which functionality
914
+ has to be changed in order for libui_paradise to enable such a unified
915
+ DSL approach. This currently does not yet work fine in 2021, but one day it
916
+ should work just fine.
917
+
918
+ Note that the older l**ibui_paradise gem** will be made available at the
919
+ least for three months (so until end of December 2021 at the least). It used
920
+ a different directory layout.
921
+
922
+ You may ask why the main module is called **LibuiParadise::Extensions**.
923
+ Why not modify LibUI directly? After all the gtk_paradise gem does the
924
+ same, by modifying **module Gtk** directly.
925
+
926
+ That is a good question, and the main reason for this is because I was
927
+ not sure whether I can actually get away with modifying the LibUI
928
+ namespace directly. Fiddle::Pointer still scares me, so I am experimenting.
929
+
930
+ For now I thought it best to create a separate module, that is then included
931
+ and modified - see the **included hook** that is presently used. This seems
932
+ to be simpler for the time being. I may plan to change a lot more one day,
933
+ if I ever manage to find out how to simulate proper subclasses via
934
+ Fiddle::Pointer ... :)
935
+
936
+ --------------------------------------------------------------------------------
937
+ ## SNIPPETS.md
938
+
939
+ Next, the content of the file called **SNIPPETS.md** will be shown.
940
+
941
+
942
+ <pre>
943
+ # How to require libui:
944
+
945
+ require 'libui'
946
+
947
+ # How to instantiate libui:
948
+
949
+ UI = LibUI
950
+ init = UI.init
951
+
952
+ # How to add a new main window:
953
+ # width, height, hasMenubar
954
+ main_window = UI.new_window('hello world', 300, 200, 1)
955
+
956
+ Source code:
957
+
958
+ https://raw.githubusercontent.com/andlabs/libui/master/unix/window.c
959
+
960
+ # How to add a libui-widget to the main window / Designate a child widget:
961
+
962
+ UI.window_set_child(main_window, button)
963
+
964
+ # Act on closing-event (on quit tag):
965
+
966
+ UI.window_on_closing(main_window) {
967
+ puts 'Bye Bye'
968
+ UI.control_destroy(main_window)
969
+ UI.quit
970
+ 0
971
+ }
972
+ # Or simpler:
973
+ close_properly(main_window)
974
+
975
+ # How to properly close via LibuiExtensions:
976
+
977
+ main_window = UI.window('Notepad', 500, 300, 1)
978
+ UI.close_properly(main_window)
979
+ # Or simpler:
980
+ close_properly(main_window)
981
+
982
+ # Add the window to the main UI:
983
+
984
+ UI.control_show(main_window)
985
+ main_window.show_the_controls # Or use this one here.
986
+
987
+ # Set the padding on a grid:
988
+
989
+ UI.grid_set_padded(grid, 25)
990
+
991
+ # Attach a new widget onto the grid:
992
+
993
+ UI.grid_append(grid, text('Yo2'), 1, 0, 1, 1, 0, 0, 1, 0)
994
+
995
+ # How to add a click-action to the button:
996
+
997
+ UI.button_on_clicked(button) {
998
+ UI.msg_box(main_window, 'Information', 'You clicked the button')
999
+ 0
1000
+ }
1001
+
1002
+ # How to add a margin:
1003
+
1004
+ UI.window_set_margined(MAIN_WINDOW, 1)
1005
+
1006
+ # How to exit easily:
1007
+
1008
+ UI.window_on_closing(main_window) {
1009
+ UI.exit_from(main_window)
1010
+ }
1011
+
1012
+ # Create an attributed String:
1013
+
1014
+ string = UI.new_attributed_string
1015
+ attributes = UI.new_family_attribute("Courier New 30")
1016
+ attribute = UI.new_color_attribute(0.75, 0.25, 0.5, 0.75)
1017
+ UI.append_with_attribute("text color", attribute, nil)
1018
+
1019
+ # How to set a padded box:
1020
+
1021
+ UI.box_set_padded(box, 1) # The value is either 0 or 1.
1022
+
1023
+ Alignment may be:
1024
+ 0: Fill
1025
+ 1: Start
1026
+ 2: Center
1027
+ 3: End
1028
+ Positioning:
1029
+ 0: Left
1030
+ 1: Top
1031
+ 2: Right
1032
+
1033
+ # Create a message-box and an error message box:
1034
+
1035
+ UI.msg_box(main_window, 'Information', 'You clicked the button')
1036
+ UI.msg_box_error(main_window, 'Information', 'You clicked the button')
1037
+
1038
+ # Add a checkbox (checkbox tag, checkbutton)
1039
+
1040
+ checkbox = UI.new_checkbox('Checkbox') # or ui_checkbox
1041
+ checkbox_toggle_callback = proc { |pointer|
1042
+ checked = UI.checkbox_checked(pointer) == 1
1043
+ UI.window_set_title(MAIN_WINDOW, "Checkbox is #{checked}")
1044
+ UI.checkbox_set_text(pointer, "I am the checkbox (#{checked})")
1045
+ 0
1046
+ }
1047
+ UI.checkbox_on_toggled(checkbox, checkbox_toggle_callback, nil)
1048
+ UI.box_append(inner, checkbox, 0)
1049
+
1050
+ To set it checked:
1051
+
1052
+ checkbox.set_checked(1)
1053
+
1054
+ To query its state:
1055
+
1056
+ checked = UI.checkbox_checked(pointer) == 1
1057
+
1058
+ # And the control:
1059
+
1060
+ UI.control_show(main_window)
1061
+
1062
+ # Using a text-entry (ui entry tag):
1063
+
1064
+ text_entry = UI.new_entry
1065
+ UI.entry_set_text(text_entry, 'Please enter your feelings')
1066
+ UI.entry_on_changed(text_entry, text_changed_callback, nil)
1067
+
1068
+ # To set this on a "multiline entry", aka spanning several
1069
+ # rows, do use:
1070
+ UI.multiline_entry_set_text(entry1, 'Yo there')
1071
+ ui_text_view # an alias used in libui_paradise
1072
+
1073
+ text1 = UI.entry_text(entry1) # Obtain text. You may have to call .to_s on it, to guarantee the String.
1074
+ UI.multiline_entry_text # Obtain the text from a multiline entry.
1075
+
1076
+ # Create a textview:
1077
+
1078
+ UI.new_non_wrapping_multiline_entry
1079
+
1080
+ # Putting the text-entry into a hbox:
1081
+
1082
+ UI.box_append(hbox1, text_entry, 1)
1083
+
1084
+ # Create a combobox (combo tag, combobox tag):
1085
+
1086
+ combobox_selected_callback = proc { |ptr|
1087
+ puts "New combobox selection: #{UI.combobox_selected(ptr)}"
1088
+ }
1089
+ cbox = UI.new_combobox
1090
+ UI.combobox_append(cbox, 'combobox Item 1')
1091
+ UI.combobox_append(cbox, 'combobox Item 2')
1092
+ UI.combobox_append(cbox, 'combobox Item 3')
1093
+ UI.box_append(inner, cbox, 0)
1094
+ UI.combobox_on_selected(cbox, combobox_selected_callback, nil)
1095
+
1096
+ # Or more concise:
1097
+ combo_box = UI.combobox {
1098
+ ['combobox Item 1', 'combobox Item 2', 'combobox Item 3']
1099
+ }
1100
+
1101
+ # Select the first entry:
1102
+
1103
+ UI.combobox_set_selected(combobox, 0) # The first one will be active too.
1104
+
1105
+ # Create a new tabbed notebook:
1106
+
1107
+ ui_tabs
1108
+ ui_notebook
1109
+
1110
+ # Add content to an editable combox:
1111
+
1112
+ UI.append() # .append() adds the named item to the end of the EditableCombobox.
1113
+
1114
+ # How to run the main loop:
1115
+
1116
+ UI.main
1117
+
1118
+ # How to quit:
1119
+
1120
+ UI.quit
1121
+
1122
+ # How to build a menu-interface (menu tag):
1123
+
1124
+ help_menu = UI.new_menu('Help')
1125
+ version_item = UI.menu_append_item(help_menu, 'Version')
1126
+
1127
+ # Create a new image:
1128
+
1129
+ image = UI.new_image(width, height)
1130
+
1131
+ # Fonts and LibUI:
1132
+
1133
+ font_descriptor = UI::FFI::FontDescriptor.malloc
1134
+ p 'Font family: '+font_descriptor.Family.to_s+
1135
+ 'Font size: '+font_descriptor.Size+
1136
+ 'Font weight: '+font_descriptor.Weight+
1137
+ 'Font italic: '+font_descriptor.Italic+
1138
+ 'Font stretch: '+font_descriptor.Stretch
1139
+
1140
+ # Available widgets in libUI:
1141
+
1142
+ Button A button with a label
1143
+ Checkbox A checkbox with a label
1144
+ Combobox A simple combobox
1145
+ ColorButton A button for selecting a color
1146
+ EditableCombobox A combobox that can be edited.
1147
+ Entry Text input, can be disabled.
1148
+ FontButton A button for selecting a font (Incomplete: Cannot set programmatically)
1149
+ Form A container that takes labels for its contents
1150
+ Grid A container that aligns widgets for window design
1151
+ Group A container that provides a title for a set of items
1152
+ Label Displays a single line of text
1153
+ Menu Creates a single column of an application menu
1154
+ MultilineEntry An entry that allows multiple lines.
1155
+ Time and Date Pickers Allows choosing of a date and/or time.
1156
+ ProgressBar Displays a progress bar
1157
+ RadioButton A set of radio buttons
1158
+ Separator A simple vertical or horizontal separator
1159
+ Slider A draggable slider for choosing a value in a range
1160
+ Spinbox A numerical input with a minimum and maximum range
1161
+ Tab A set named tabs for placing items in
1162
+ Window Contains any other widget, cannot be embedded in a container
1163
+ VBox, HBox A vertical or horizontal box for grouping items
1164
+
1165
+ </pre>
1166
+
1167
+
1168
+ --------------------------------------------------------------------------------
1169
+
1170
+ ## Advantages and Disadvantages of the libui project
1171
+
1172
+ It would be unfair to only selectively name advantages but not talk about
1173
+ disadvantages, so this subsection will show some limitations, trade-offs,
1174
+ constraints and opportunities. This is not complete, but it may become
1175
+ somewhat more complete over time. Stay tuned.
1176
+
1177
+ (a) Advantages:
1178
+
1179
+ - Works on windows out-of-the-box after you installed the libui-gem.
1180
+ - Is super-simple to use compared to other toolkits, including ruby-gtk.
1181
+ - Super-simple to build up a prototype for a GUI, buttons that work,
1182
+ spin-boxes, text-views and so forth. Faster than any other toolkit
1183
+ IMO.
1184
+ - Works cross-platform.
1185
+
1186
+ (b) Disadvantage:
1187
+
1188
+ - Limited ability to control the layout and size of widgets.
1189
+ - May look like utter crap ... :-)
1190
+ - Some functionality is missing, such as a scrolled-window for every widget.
1191
+ - No way to use different fonts in the same application and choosing a font
1192
+ is needlessly complicated.
1193
+
1194
+ Some more disadvantages relate to Fiddle::Pointer. You kind of need to
1195
+ know C fairly well as well as the GC in ruby, in order to understand
1196
+ what is going on. Since I don't, I hit a dead end, kind of.
1197
+
1198
+ This is so far in September 2021. Let's see what the future brings.
1199
+ Perhaps other toolkits will learn from libui and implement the good
1200
+ parts for **their own** widget set.
1201
+
1202
+ ## LibuiParadise.parse_this_config_file()
1203
+
1204
+ This method can be used to parse a .config file. This file should
1205
+ describe the main window, such as:
1206
+
1207
+ width: 1000
1208
+ height: 150
1209
+ title: Parse Config File Example
1210
+
1211
+ I have been using this in gtk_paradise and I think it may be convenient
1212
+ if you create lots of small windows and widgets that you don't want
1213
+ to hardcode values for directly into the .rb file at hand.
1214
+
1215
+ To create a toplevel window from this use code such as the following:
1216
+
1217
+ use_this_config_file = '023_parse_config_file_example.config'
1218
+ window = LibuiParadise.parse_this_config_file(use_this_config_file)
1219
+
1220
+ In fact, the example **023_parse_config_file_example.rb** shows how
1221
+ this is used. The width of the main window will be 1000 and the
1222
+ height only 150.
1223
+
1224
+ This functionality may be extended in the future. For example, we
1225
+ could enable to automatically parse such a .config file if it
1226
+ exists, thus not even requiring that method call in the future.
1227
+
1228
+ For now, though, you have to explicitely use that method if you
1229
+ want to instantiate such a window from a .config file. It will
1230
+ be evaluated at a later time (past September 2021) how useful this
1231
+ really is.
1232
+
1233
+ ## Width and Height of the main window
1234
+
1235
+ Different computers have different display values (their monitor).
1236
+
1237
+ I use a wide-screen LCD monitor, but I also have smaller laptops,
1238
+ and I want libui to work there just as well.
1239
+
1240
+ The following API can be used to set the width and height of the
1241
+ main window:
1242
+
1243
+ set_height()
1244
+ set_width()
1245
+
1246
+ You can also use something like '95%' as input. In that case the
1247
+ desired value will be calculated depending on the max-resolution
1248
+ of the current display. This presently only works on linux; if
1249
+ someone knows how to make this work on windows and Mac OSX let
1250
+ me know. (On these systems it will instead default to a hardcoded
1251
+ value of 1024 for width and 800 for height).
1252
+
1253
+ The following example shows how to use a percentage value:
1254
+
1255
+ set_height('80%')
1256
+
1257
+ ## Coloured Text
1258
+
1259
+ At this point I only show how this may look on Win7, re-using
1260
+ the picture the kotlin-libui developers made available:
1261
+
1262
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/drawtext/drawtext-windows7.png" style="margin-left: 2em">
1263
+
1264
+ Doesn't look that bad, right? May not be the prettiest GUI of all
1265
+ times, but it is functional - and simple.
1266
+
1267
+ ## Status of the libui_paradise project
1268
+
1269
+ I am still, as of 2021, experimenting somewhat randomly. While I intend to
1270
+ improve libui_paradise, it is a hobby project that is not as important
1271
+ as, say, the gtk_paradise project (which contains custom ruby-gtk
1272
+ related addons; ruby-gtk has a LOT more functionality than libui has).
1273
+
1274
+ I can not promise to work reliably on libui_paradise, but every now and
1275
+ then, say every some weeks, I will try to add more code to it, in
1276
+ particular examples and documentation. However had, keep in mind that
1277
+ the project is not one of my most important projects, so it may not
1278
+ receive as many updates as other projects. See also the next subsection,
1279
+ the "Todo List".
1280
+
1281
+ ## DSLs for libui (in ruby)
1282
+
1283
+ kojix2 pointed out that glimmer has support for libui; check it out
1284
+ here:
1285
+
1286
+ https://github.com/AndyObtiva/glimmer-dsl-libui
1287
+
1288
+ libui_paradise will support the same syntax (in the long run) as
1289
+ glimmer does, either directly, or via a module and a way to
1290
+ require it specifically. But stay tuned for this - right now as
1291
+ of late 2021 this is not yet guaranteed. (If anyone needs quick
1292
+ API changes, let me know and I'll change libui_paradise. Other
1293
+ than that, the libui_paradise project is in a slow maintenance
1294
+ mode right now, so again, stay tuned. \o/ )
1295
+
1296
+ ## Todo List and Goals related to libui
1297
+
1298
+ Here I will list some todo entries and related goals for libui or the
1299
+ libui_paradise gem - if anyone finds out how to solve some of these
1300
+ limitations, you are welcome to share!
1301
+
1302
+ - Add more small, **standalone examples** showcasing how to use (and
1303
+ combine) different functionality, in particular colourized text and use
1304
+ of (embedded) images if applicable. Ideally we should be able to combine
1305
+ both freely, if possible.
1306
+
1307
+ - Find out how to create "proper" subclasses to Fiddle::Pointer, simulating
1308
+ a subclass to it.
1309
+
1310
+ - Find out how halign and valign work in Libui-Grid. Somehow I can not easily reposition it ...
1311
+ or perhaps I have been able to, and just have not noticed this (may have to embed one
1312
+ widget in another widget in order for this to come into effect)
1313
+
1314
+ - Find out how to do ad-hoc calls on linux to gtk, so that we can use CSS.
1315
+ Perhaps some extension mechanism could be used, similar to how ffi works
1316
+ in general. Unfortunately this probably requires knowledge of C, so this
1317
+ is an obstacle for me. I should have learned C before ruby ... :P
1318
+
1319
+ - Get more people to learn about libui and use them in their projects,
1320
+ in particular for simple applications. I think this is the most important
1321
+ goal: we need more people to learn about libui and begin to use it.
1322
+ Contribute to upstream as well. If we have a sufficiently large user
1323
+ base then it should be easier to add new possibilities onto libui,
1324
+ which in turn will "cascade" downwards to all the other bindings
1325
+ to libui, be it in kotlin, python, ruby and so forth.
1326
+
1327
+ ## DateTime Widget
1328
+
1329
+ Only an image is shown how it may look on windows:
1330
+
1331
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/datetime/datetime-windows7.png" style="margin-left: 2em">
1332
+
1333
+ ## Password entries in LibUI
1334
+
1335
+ If you use the libui_paradise gem then you can create a new password entry
1336
+ in using any of the following variants:
1337
+
1338
+ entry = ui_password_entry
1339
+ entry = password_entry
1340
+
1341
+ For "raw" libui, use this:
1342
+
1343
+ LibUI.new_password_entry
1344
+
1345
+ ## Progress Bars in LibUI
1346
+
1347
+ Here an image how this may look:
1348
+
1349
+ <img src="https://i.imgur.com/i1i4ppZ.png" style="margin-left: 2em">
1350
+
1351
+ ## Tables in LibUI
1352
+
1353
+ You may be able to create a new table via:
1354
+
1355
+ table = LibUI.new_table
1356
+
1357
+ model = LibUI.new_table_model(model_handler)
1358
+
1359
+ table_params = LibUI::FFI::TableParams.malloc
1360
+ table_params = Fiddle::RUBY_FREE
1361
+ table_params.Model = model
1362
+ table_params.RowBackgroundColorModelColumn = -1
1363
+ table = LibUI.new_table(table_params)
1364
+
1365
+ The table header is an array that contains the following attributes:
1366
+
1367
+ 1. editable, bool type, the column is whether editable
1368
+ 2. textColor
1369
+ 3. title
1370
+ 4. type, specify value of button, image, imgtext, progress, checkbox, checkboxtext, color, text
1371
+
1372
+ Note that this is incomplete; it's a bit complicated.
1373
+
1374
+ In the end, this is possible though:
1375
+
1376
+ <img src="https://raw.githubusercontent.com/msink/kotlin-libui/master/samples/table/table-windows7.png" style="margin-left: 2em">
1377
+
1378
+ ## Form example in LibuiParadise
1379
+
1380
+ Inspired by **glimmer-libui**, I ported the form example, into
1381
+ **027_form_example.rb**. Unfortunately it does not look
1382
+ as good as it does in **glimmer-libui**, and the code I use for
1383
+ it is too verbose right now.
1384
+
1385
+ Still, if you are curious, this is how it looks on icewm
1386
+ in October 2021:
1387
+
1388
+ <img src="https://i.imgur.com/FkU6aWd.png" style="margin-left: 2em">
1389
+
1390
+ I may improve this eventually a bit, so that the alignment looks
1391
+ as good as it does on glimmer-dsl; the code of 27_form_example.rb
1392
+ may also be cleaned up in the future (the glimmer-libui code
1393
+ looks better). But for now this is how it is; Andy is very
1394
+ actively improving **glimmer-libui** right now.
1395
+
1396
+ Available "new"-widgets in LibUI:
1397
+
1398
+ LibUI.new_area
1399
+ LibUI.new_attributed_string
1400
+ LibUI.new_group
1401
+ LibUI.new_spinbox
1402
+ LibUI.new_stretch_attribute
1403
+ LibUI.new_background_attribute
1404
+ LibUI.new_button # this is a simple button
1405
+ LibUI.new_checkbox # this is a simple checkbox
1406
+ LibUI.new_color_attribute
1407
+ LibUI.new_color_button
1408
+ LibUI.new_combobox # this is a combobox
1409
+ LibUI.new_date_picker
1410
+ LibUI.new_date_time_picker
1411
+ LibUI.new_editable_combobox
1412
+ LibUI.new_grid
1413
+ LibUI.new_horizontal_box
1414
+ LibUI.new_horizontal_separator # this is a simple horizontal separator
1415
+ LibUI.new_image # this is a simple image
1416
+ LibUI.new_italic_attribute # this is basically italic font-style
1417
+ LibUI.new_label
1418
+ LibUI.new_tab
1419
+ LibUI.new_table # this is a simple table
1420
+ LibUI.new_table_model
1421
+ LibUI.new_table_value_image
1422
+ LibUI.new_table_value_int
1423
+ LibUI.new_table_value_color
1424
+ LibUI.new_table_value_string
1425
+ LibUI.new_time_picker
1426
+ LibUI.new_menu
1427
+ LibUI.new_multiline_entry # this is a textview
1428
+ LibUI.new_non_wrapping_multiline_entry
1429
+ LibUI.new_open_type_features
1430
+ LibUI.new_password_entry
1431
+ LibUI.new_underline_attribute
1432
+ LibUI.new_entry
1433
+ LibUI.new_progress_bar # this is a progress_bar
1434
+ LibUI.new_underline_color_attribute
1435
+ LibUI.new_family_attribute
1436
+ LibUI.new_radio_buttons
1437
+ LibUI.new_vertical_box
1438
+ LibUI.new_features_attribute
1439
+ LibUI.new_scrolling_area # this is a scrolling area
1440
+ LibUI.new_vertical_separator
1441
+ LibUI.new_font_button
1442
+ LibUI.new_search_entry # this is a search entry
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
1448
+
1449
+ ## Links related to libui or libui-based projects
1450
+
1451
+ This subsection may contain a few links, in the event that other
1452
+ people want to see useful entries.
1453
+
1454
+ I will try to explain what is to be seen by these various pages.
1455
+
1456
+ (1) https://wiki.call-cc.org/eggref/4/libui
1457
+
1458
+ This is using chicken-egg scheme for bindings to libui. I like the
1459
+ simplicity and overview - it's really nice to read and use. I'd
1460
+ wish we would have this for ruby too. :)
1461
+
1462
+ Note that the documentation is outdated as of 2021, though. A fate
1463
+ shared with a lot of documentation in general out there ...
1464
+
1465
+ (2) https://pkg.go.dev/github.com/andlabs/ui
1466
+
1467
+ This is andlabs' documentation for libui, from the point of view of
1468
+ **Go**. It is probably the biggest, most complete documentation for
1469
+ libui. While it is specific to Go, as it was written by the same
1470
+ author you can expect the documentation to be quite decent.
1471
+
1472
+ (Note that some other bindings contain good documentation too,
1473
+ such as kotlin libui bindings and others.)
1474
+
1475
+ (3) http://api.call-cc.org/4/doc/libui
1476
+
1477
+ Similar to the first one, but uses a different layout, which
1478
+ may be helpful.
1479
+
1480
+
1481
+ ## Contact information
1482
+
1483
+ If your creative mind has ideas and specific suggestions to make this
1484
+ gem more useful in general, feel free to drop me an email at any
1485
+ time, via:
1486
+
1487
+ shevy@inbox.lt
1488
+
1489
+ Before that email I used an email account at Google gmail, but in **2021** I
1490
+ decided to slowly abandon gmail for various reasons. In part this is because
1491
+ the UI annoys me (on non-chrome browser loading takes too long), but also
1492
+ because of Google's attempt to establish mass surveillance via its
1493
+ federated cohorts sniffing (FLoC). I do not know what happened at Google,
1494
+ but enough is enough - there is only so much you can take while supporting
1495
+ greed. When it comes to data mining done by private groups, ultimately
1496
+ the user became the product.
1497
+
1498
+ Do keep in mind that responding to emails may take some time,
1499
+ depending on the amount of work I may have at that moment, due
1500
+ to reallife time constraints. I will, however had, read feedback
1501
+ eventually. Patches and code changes are welcome too, of course,
1502
+ as long as they are in the spirit of the project at hand, e. g.
1503
+ fitting to the general theme. For this I may make use of github
1504
+ as a discussion site, but this has a low priority right now.
1505
+