/**
\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":
\code
#include
#include
#include
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.
\code
#include "CubeView.h"
#include
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 New->Code->Class
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 New->Code->Function/Method. 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
New->Group->Window. 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
New->Other->Box 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 Class: text
entry box. This tells FLUID that it is not an Fl_Box, but a
similar widget with the same constructor.
In the Extra Code: field enter \#include "CubeView.h"
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 Callback
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 cube->redraw() 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
New->Code->Function/Method. 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
New->Code->Code. 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
New->Code->Code. 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 Edit->Preferences.
At the bottom of the preferences dialog box is the key:
"Include Header from Code".
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.
*/