summaryrefslogtreecommitdiff
path: root/fluid/documentation/src/page_tutorial.dox
blob: 3f0d48cae297086d84ea4f39acaa640a153786cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
/**

 \page page_tutorial Tutorials

 \tableofcontents

<!-- ---------------------------------------------------------------------- -->

 \section fluid_hello_world_tutorial Hello, World!

 The first FLUID tutorial explains the FLUID basics. It creates a single
 `main()` function that opens an application window with a static text box
 inside.

 After launching FLUID we want to make sure that two very useful tool windows
 are open.
 The "Widget Bin" gives quick access to all available widgets and functional
 types. It can be opened via the main menu: __Edit > Show Widget Bin__, or
 using the shortcut __Alt-B__.

 The second very helpful tool box is the "Code View". The Code View gives
 a preview of the code as it will be generated by FLUID. All changes in the
 layout or in attributes are reflected immediately in the Code View.
 Choose __Edit > Show Code View__ or press __Alt-C__ to get this
 toolbox. Make sure that _Auto-Refresh_ and _Auto-Position_ are active in
 the Code View.

 Let's start Hello World from scratch. If there is already a previous project
 loaded, select __File > New__ or __Ctrl-N__.

 Before we can create a window, we need a "C" function that can be called
 when we run the program. Select __New > Code > Function/Method...__ or click on
 the
 \image{inline} html flFunction.png "Function/Method"
 \image{inline} latex flFunction.png
 image in the widget bin.

 A function is added as a first line to the widget tree in the main window,
 and a "Function/Method Properties" dialog box will pop up. For our Hello World
 program, delete all text in the top "Name(args)" text field. If this field
 is left empty, FLUID will generate a `main(argc, argv)` function for us.

 \image html fluid4.png "Function/Method Properties Dialog"
 \image latex fluid4.png "Function/Method Properties Dialog" width=7cm

 Click OK to apply the changes you made in the Function Properties dialog.
 You can get this dialog back at any time by selecting the function in the
 main window and pressing __F1__, or by double-clicking it.

 Note that the function will not show up in the Code View yet. FLUID will
 not generate code for functions that don't have any content, and only create
 a forward declaration in the header file, assuming that the function will
 be implemented inside another module.

 Keep the `main` function selected and add an `Fl_Window` to the function by
 selecting __New > Group > Window...__, by clicking the
 \image{inline} html flWindow.png "Group/Window"
 \image{inline} latex flWindow.png
 image in the Widget Bin, or by dragging the Group/Window image from the
 Widget Bin onto the desktop.

 A new application window will open up on the desktop. The thin red outline
 around the window indicates that it is selected. Dragging the red line will
 resize the window. Take a look at the Code View: the main function
 is now generated, including code to instantiate our `Fl_Window`.

 To edit all the attributes of our window, make sure it is selected and press
 __F1__, or double-click the entry in the main FLUID window, or double-click
 the window itself. The "Widget Properties" dialog box will pop up. Enter
 some text in the "Label:" text box and see how the label is updated immediately
 in the window itself, in the widget list, and in the Code View.

 Adding a static text to our window is just as easy. Put an `Fl_Box` into our
 window by selecting __New > Other > Box__, or by clicking on the
 \image{inline} html flBox.png "Other/Box"
 \image{inline} latex flBox.png
 image in the Widget Bin, or by dragging the Other/Box image from the
 Widget Bin into our newly created window.

 Most importantly, enter the text "Hello, World!" in the "Label:" field
 of the Box Widget Properties panel to achieve the goal of this tutorial. Now
 is also a great time to experiment with label styles, label fonts, sizes,
 colors, etc. .

 Finally, we should save our project as an `.fl` project file somewhere. Once
 the project is saved, select __File > Write Code__ or press __Shift-Ctrl-C__
 to write our source code and header file to the same directory.

 Compile the program using a Makefile, CMake file, or fltk-config as described
 in the FLTK manual and the `README.txt` files in the FLTK source tree.

<!-- ---------------------------------------------------------------------- -->

 \section fluid_1of7guis_tutorial 7GUIs, Task 1

 In the first "Hello World" tutorial, we built an entire application in FLUID.
 It's a boring application though that does nothing except quitting when the
 close button in the window border is clicked.

 \image html 1of7GUIs.png "Task 1 of 7GUIs"
 \image latex 1of7GUIs.png "Task 1 of 7GUIs" width=5cm

 The second tutorial will introduce callbacks by implementing task 1, "Counter",
 of 7GUIs. 7GUIs has been created as a spin-off of my master’s thesis
 Comparison of Object-Oriented and Functional Programming for GUI Development
 at the Human-Computer Interaction group of the Leibniz Universität Hannover
 in 2014. 7GUIs defines seven tasks that represent typical challenges in GUI
 programming. https://eugenkiss.github.io/7guis/ .

 Task 1 requires "Understanding the basic ideas of a language/toolkit. The
 task is to build a frame containing a label or read-only textfield T and a
 button B. Initially, the value in T is “0” and each click of B increases the
 value in T by one."

 Our knowledge from tutorial 1 is enough to generate the `main()` function, and
 add an `Fl_Window`, an `Fl_Output`, and an `Fl_Button`. To make life easy,
 FLUID comes with a built-in template for this tutorial. Just select
 __File > New from Template...__ and double-click "1of7GUIs" in the Template
 Panel.

 We will need to reference the output widget in our callback, so let's assign a
 pointer to the widget to a global variable and give that variable a name.
 Open the Widget Properties dialog by double-clicking the output widget.
 Change to the "C++" tab, and enter "`counter_widget`" in the "Name:" field.

 The "Count" button is the active element in our application. To tell the
 app what to do when the user clicks the button, we create a callback function
 for that button. Open the widget properties dialog for the button.
 In the "C++" tab, we find the input field "Callback:".

 The callback is called exactly once every time the user clicks the button. Our
 strategy here is to read the current value from the `counter_widget`,
 increment it by 1, and write it back to `counter_widget`.
 The FLTK documentation tells us that we can use `Fl_Output::ivalue()` to get
 text in `Fl_Output` as an integer, and we can write it back by calling
 `Fl_Output::value(int)`. When the value is changed, FLTK will automatically
 redraw the output widget for us. So here is the callback code:

 ```
 int i = counter_widget->ivalue();
 i++;
 counter_widget->value(i);
 ```

 That's it. This is a complete interactive desktop application. Compile, link,
 run, and test it to see how it works.

<!-- ---------------------------------------------------------------------- -->

\section fluid_cubeview_tutorial Cube View

This tutorial will show you how to generate a complete user interface
class with FLUID that is used for the CubeView program provided with FLTK.

\image html cubeview.png "CubeView demo"
\image latex cubeview.png "CubeView demo" width=7cm

The window is of class CubeViewUI, and is completely generated by FLUID,
including class member functions. The central display of the cube is a
separate subclass of Fl_Gl_Window called CubeView. CubeViewUI manages
CubeView using callbacks from the various sliders and rollers to
manipulate the viewing angle and zoom of CubeView.

At the completion of this tutorial you will (hopefully) understand how to:

-# Use FLUID to create a complete user interface class, including
   constructor and any member functions necessary.
-# Use FLUID to set callback member functions of custom widget classes.
-# Subclass an Fl_Gl_Window to suit your purposes.

\subsection fluid_cubeview The CubeView Class

The CubeView class is a subclass of Fl_Gl_Window. It has methods for
setting the zoom, the \e x and \e y pan, and the rotation angle
about the \e x and \e y axes.

You can safely skip this section as long as you realize that CubeView
is a sublass of Fl_Gl_Window and will respond to calls from
CubeViewUI, generated by FLUID.

\par The CubeView Class Definition

Here is the CubeView class definition, as given by its header file
"test/CubeView.h":
<br>

<!-- Code copied from test/CubeView.h -->
\code
#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>

class CubeView : public Fl_Gl_Window {

public:
  CubeView(int x, int y, int w, int h, const char *l = 0);

  // This value determines the scaling factor used to draw the cube.
  double size;

  /* Set the rotation about the vertical (y) axis.
   *
   * This function is called by the horizontal roller in
   * CubeViewUI and the initialize button in CubeViewUI.
   */
  void v_angle(double angle) { vAng = angle; }

  // Return the rotation about the vertical (y) axis.
  double v_angle() const { return vAng; }

  /* Set the rotation about the horizontal (x) axis.
   *
   * This function is called by the vertical roller in
   * CubeViewUI and the initialize button in CubeViewUI.
   */

  void h_angle(double angle) { hAng = angle; }

  // The rotation about the horizontal (x) axis.
  double h_angle() const { return hAng; }

  /* Sets the x shift of the cube view camera.
   *
   * This function is called by the slider in CubeViewUI
   * and the initialize button in CubeViewUI.
   */
  void panx(double x) { xshift = x; }

  /* Sets the y shift of the cube view camera.
   *
   * This function is called by the slider in CubeViewUI
   * and the initialize button in CubeViewUI.
   */
  void pany(double y) { yshift = y; }

  /* The widget class draw() override.
   *
   * The draw() function initializes Gl for another round of
   * drawing, then calls specialized functions for drawing each
   * of the entities displayed in the cube view.
   */
  void draw();

private:
  /*  Draw the cube boundaries.
   *
   * Draw the faces of the cube using the boxv[] vertices,
   * using GL_LINE_LOOP for the faces.
   */
  void drawCube();

  double vAng, hAng;
  double xshift, yshift;

  float boxv0[3]; float boxv1[3];
  float boxv2[3]; float boxv3[3];
  float boxv4[3]; float boxv5[3];
  float boxv6[3]; float boxv7[3];
};
\endcode

\par The CubeView Class Implementation

Here is the CubeView implementation. It is very similar to the
"CubeView" demo included with FLTK.
<br>

<!-- Code copied from test/CubeView.cxx -->
\code
#include "CubeView.h"
#include <math.h>

CubeView::CubeView(int x, int y, int w, int h, const char *l)
  : Fl_Gl_Window(x, y, w, h, l)
{
  Fl::use_high_res_GL(1);
  vAng = 0.0;
  hAng = 0.0;
  size = 10.0;
  xshift = 0.0;
  yshift = 0.0;

  /* The cube definition. These are the vertices of a unit cube
   * centered on the origin.*/

  boxv0[0] = -0.5; boxv0[1] = -0.5; boxv0[2] = -0.5;
  boxv1[0] =  0.5; boxv1[1] = -0.5; boxv1[2] = -0.5;
  boxv2[0] =  0.5; boxv2[1] =  0.5; boxv2[2] = -0.5;
  boxv3[0] = -0.5; boxv3[1] =  0.5; boxv3[2] = -0.5;
  boxv4[0] = -0.5; boxv4[1] = -0.5; boxv4[2] =  0.5;
  boxv5[0] =  0.5; boxv5[1] = -0.5; boxv5[2] =  0.5;
  boxv6[0] =  0.5; boxv6[1] =  0.5; boxv6[2] =  0.5;
  boxv7[0] = -0.5; boxv7[1] =  0.5; boxv7[2] =  0.5;
}

void CubeView::drawCube() {
/* Draw a colored cube */
#define ALPHA 0.5
  glShadeModel(GL_FLAT);

  glBegin(GL_QUADS);
    glColor4f(0.0, 0.0, 1.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv1);
    glVertex3fv(boxv2);
    glVertex3fv(boxv3);

    glColor4f(1.0, 1.0, 0.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv4);
    glVertex3fv(boxv5);
    glVertex3fv(boxv1);

    glColor4f(0.0, 1.0, 1.0, ALPHA);
    glVertex3fv(boxv2);
    glVertex3fv(boxv6);
    glVertex3fv(boxv7);
    glVertex3fv(boxv3);

    glColor4f(1.0, 0.0, 0.0, ALPHA);
    glVertex3fv(boxv4);
    glVertex3fv(boxv5);
    glVertex3fv(boxv6);
    glVertex3fv(boxv7);

    glColor4f(1.0, 0.0, 1.0, ALPHA);
    glVertex3fv(boxv0);
    glVertex3fv(boxv3);
    glVertex3fv(boxv7);
    glVertex3fv(boxv4);

    glColor4f(0.0, 1.0, 0.0, ALPHA);
    glVertex3fv(boxv1);
    glVertex3fv(boxv5);
    glVertex3fv(boxv6);
    glVertex3fv(boxv2);
  glEnd();

  glColor3f(1.0, 1.0, 1.0);
  glBegin(GL_LINES);
    glVertex3fv(boxv0);
    glVertex3fv(boxv1);

    glVertex3fv(boxv1);
    glVertex3fv(boxv2);

    glVertex3fv(boxv2);
    glVertex3fv(boxv3);

    glVertex3fv(boxv3);
    glVertex3fv(boxv0);

    glVertex3fv(boxv4);
    glVertex3fv(boxv5);

    glVertex3fv(boxv5);
    glVertex3fv(boxv6);

    glVertex3fv(boxv6);
    glVertex3fv(boxv7);

    glVertex3fv(boxv7);
    glVertex3fv(boxv4);

    glVertex3fv(boxv0);
    glVertex3fv(boxv4);

    glVertex3fv(boxv1);
    glVertex3fv(boxv5);

    glVertex3fv(boxv2);
    glVertex3fv(boxv6);

    glVertex3fv(boxv3);
    glVertex3fv(boxv7);
  glEnd();
} // drawCube

void CubeView::draw() {
  if (!valid()) {
    glLoadIdentity();
    glViewport(0, 0, pixel_w(), pixel_h());
    glOrtho(-10, 10, -10, 10, -20050, 10000);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();

  glTranslatef((GLfloat)xshift, (GLfloat)yshift, 0);
  glRotatef((GLfloat)hAng, 0, 1, 0);
  glRotatef((GLfloat)vAng, 1, 0, 0);
  glScalef(float(size), float(size), float(size));

  drawCube();

  glPopMatrix();
}
\endcode

\subsection fluid_cubeview_ui The CubeViewUI Class

We will completely construct a window to display and control the
CubeView defined in the previous section using FLUID.

\par Defining the CubeViewUI Class

Once you have started FLUID, the first step in defining a class is to
create a new class within FLUID using the <b>New->Code->Class</b>
menu item. Name the class "CubeViewUI" and leave the subclass blank.
We do not need any inheritance for this window. You should see the
new class declaration in the FLUID browser window.

\image html  fluid1.png "FLUID file for CubeView"
\image latex fluid1.png "FLUID file for CubeView" width=7cm

\par Adding the Class Constructor

Click on the CubeViewUI class in the FLUID window and add a new method
by selecting <b>New->Code->Function/Method.</b> The name of the
function will also be CubeViewUI. FLUID will understand that this will
be the constructor for the class and will generate the appropriate
code. Make sure you declare the constructor public.

Then add a window to the CubeViewUI class. Highlight the name of
the constructor in the FLUID browser window and click on
<b>New->Group->Window</b>. In a similar manner add the
following to the CubeViewUI constructor:

\li A horizontal roller named \p hrot
\li A vertical roller named \p vrot
\li A horizontal slider named \p xpan
\li A vertical slider named \p ypan
\li A horizontal value slider named \p zoom

None of these additions need be public. And they shouldn't be
unless you plan to expose them as part of the interface for
CubeViewUI.

When you are finished you should have something like this:

\image html fluid2.png "FLUID window containing CubeView demo"
\image latex fluid2.png "FLUID window containing CubeView demo" width=7cm

We will talk about the \p show() method that is highlighted
shortly.

\par Adding the CubeView Widget

What we have is nice, but does little to show our cube. We have already
defined the CubeView class and we would like to show it within the
CubeViewUI.

The CubeView class inherits the Fl_Gl_Window class, which
is created in the same way as an Fl_Box widget. Use
<b>New->Other->Box</b> to add a square box to the main window.
This will be no ordinary box, however.

The Box properties window will appear. The key to letting CubeViewUI
display CubeView is to enter CubeView in the <b>Class:</b> text
entry box. This tells FLUID that it is not an Fl_Box, but a
similar widget with the same constructor.

In the <b>Extra Code:</b> field enter <tt>\#include "CubeView.h"</tt>

This \p \#include is important, as we have just included
CubeView as a member of CubeViewUI, so any public CubeView methods are
now available to CubeViewUI.

\image html fluid3-cxx.png "CubeView methods"
\image latex fluid3-cxx.png "CubeView methods" width=7cm

\par Defining the Callbacks

Each of the widgets we defined before adding CubeView can have
callbacks that call CubeView methods. You can call an external
function or put a short amount of code in the <b>Callback</b>
field of the widget panel. For example, the callback for the
\p ypan slider is:

\code
cube->pany(((Fl_Slider *)o)->value());
cube->redraw();
\endcode

We call <tt>cube->redraw()</tt> after changing the value to update
the CubeView window. CubeView could easily be modified to do this, but
it is nice to keep this exposed. In the case where you may want to do
more than one view change only redrawing once saves a lot of time.

There is no reason to wait until after you have added CubeView to
enter these callbacks. FLUID assumes you are smart enough not to refer
to members or functions that don't exist.

\par Adding a Class Method

You can add class methods within FLUID that have nothing to do with the
GUI. As an example add a show function so that CubeViewUI can actually
appear on the screen.

Make sure the top level CubeViewUI is selected and select
<b>New->Code->Function/Method</b>. Just use the name
\p show(). We don't need a return value here, and since we will
not be adding any widgets to this method FLUID will assign it a return
type of \p void.

\image html fluid4.png "CubeView constructor"
\image latex fluid4.png "CubeView constructor" width=7cm

Once the new method has been added, highlight its name and select
<b>New->Code->Code.</b> Enter the method's code in the code window.

\subsection fluid_addconst Adding Constructor Initialization Code

If you need to add code to initialize a class, for example setting
initial values of the horizontal and vertical angles in the
CubeView, you can simply highlight the constructor and select
<b>New->Code->Code</b>. Add any required code.

\subsection fluid_gencode Generating the Code

Now that we have completely defined the CubeViewUI, we have to generate
the code. There is one last trick to ensure this all works. Open the
preferences dialog from <b>Edit->Preferences</b>.

At the bottom of the preferences dialog box is the key:
<b>"Include Header from Code"</b>.
Select that option and set your desired file
extensions and you are in business. You can include the CubeViewUI.h
(or whatever extension you prefer) as you would any other C++ class.

*/