libui_paradise 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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
+