* CMake integration, no autotiools * alignment panel is now correctly renamed to setting panel * source view is now correctly renamed to code view * Merge FLTK FLUID docs into FLUID user manual. * Add two simple entry tutorials * Remove FLUID chapter form FLTK docs. * GitHub action to generate HTML and PDF docs and make the available as artefacts
535 lines
18 KiB
Plaintext
535 lines
18 KiB
Plaintext
/**
|
||
|
||
\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.
|
||
|
||
*/
|