libui_paradise 0.2.48

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

Potentially problematic release.


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

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