Fluid (the FL User Interface Designer) is a graphical editor that is used to produce FL source code. Fluid edits and saves it's state in ".fl" files. These files are text, and you could (with care) edit them in a text editor, perhaps to get some special effects. When asked to "compile", fluid outputs a .C source file and a .H header file: The .C file contains one or more public functions, each of which will create one or more FL windows and all the objects in those windows. The .H file declares (as externs) all the functions and named objects created by the .C file, and includes all the necessary FL header files for those objects. The C file must be compiled and linked with a "main" source file(s) that you write. This source code must include the .H output, and should call the functions in the .C file to create windows. The main code must do show() on the windows and run the Fl::wait() loop. _________ / / __________ +->/.C file /--------+ / / / /________/ | /.fl file /<==>[fluid]< #include | /_________/ \ ___v_____ | \ / / | +>/.H file / | /________/ | ^ | #include | ___|_____ | __________ / / V / / / main.C /--->[c++,link]-->/ program / /________/ /_________/ Objects created by fluid are either "named" or "unnamed". If they are named, the .C file will declare a global variable with that name of type "*". This pointer has a value of zero until the fluid function is called, the fluid function will set it to the instance of the . Unnamed objects are only accessible through pointers from other objects. Windows may be named or unnamed. Named windows are only created once even if you call the function several times (fluid outputs "if (!name) {...}" around the code that creates the window). Unnamed windows lets you create many instances of the same window structure, a pointer to the unnamed window is returned from the fluid function (you can only put one unnamed window in a function). Objects may either call a named callback function that you write in another source file, or you can supply a small piece of C++ source and fluid will write a private callback function into the .C file. ================================================================ Worlds shortest tutorial: ================================================================ Type "fluid&" Pick "New/Function" off the menu. Delete the function name in the popup window and hit OK. The text "main()" with a triangle next to it should appear highlighted in the main window. Pick "New/Window" off the menu. Move the window and resize it to the size you want. Pick "New/buttons/Button" off the menu. Hit the "OK" button to dismiss the panel that appears. In the window you created, try moving the button by dragging it around. Notice that it "snaps" to fixed locations. If you want to drag it smoothly, hold down Alt. You can also change the size of the steps with Edit/Preferences. Try resizing the object by dragging the edges and corners. Type Alt+c to copy the object. Type Alt+v to paste a copy into the window. Type Alt+v several times. Drag the objects and resize them so they don't overlap. Notice that you have to click an object to pick it first, then drag it. Try selecting several objects by dragging a box around them. Check what happens when you move them, or when you drag an edge to resize them. You can also use Shift+click to toggle objects on and off. You can also select objects by clicking on them in the list in the main window, try that. Double-click one of the buttons. You will get a control panel. Try changing the "label". Try changing other items near the top of the panel. To see any changes to the box type clearer, type "Alt+o" to make the red overlay disappear. Type "#include " into the first line of "extra code:". Type "exit(0);" into the "callback:". Hit OK. Pick "File/Save As" off the menu. Type "test.fl" into the file chooser and hit return. Pick "File/Write Code" off the menu. Go back to your terminal window. Type "more test.C" and "more test.H" and you can see the code it made. Type "make test" (you may have to add libaries to your Makefile). Type "./test" to run your program. Try the buttons. The one you put the code into will exit the program. Type "Alt+Q" to exit fluid. Ok, now try to make a real program. ================================================================ This code is quite a kludge and probably impossible to figure out. I hope it will be fixed someday, but my intention was to make FL itself clean and simple, even if the user interface designer is forced to be more complex as a result. An object in fluid is represented by a subclass of "Fl_Type". Creating a new instance of an object is done by calling the virtual method "make()" on Fl_Type. To allow an initial version of each type to be created, there is a static "factory" instance of every subclass of Fl_Type. For now, make() is only called on these. The intention was to use make() to copy objects, but that did not happen. Instead I write the descriptions and read them back from a file in /usr/tmp, which is much more reliable because the read/write code is tested every time a file is saved! The (non-factory) instances are linked into a list by the previous and next pointers. The "hierarchy" is stored by a "level" number on each object, and an "isparent" flag on the parent. To find the "brother" of an object, code must search forward for the next object whose level is equal to this one. A null pointer or an object with a lower level number indicates no brother. If the type is a subclass of Fl_Object, the "o" pointer points at an Fl_Object. This allows the code in FL to be used to draw the object. The user_data() field on the Fl_Object is used as a back pointer to the Fl_Type. The "factory" has an "o" pointer that points at the "template object". This is an Fl_Object created the first time make() is called and is used to figure out what the default values for the fields are for the object. This "o" pointer exists in all Fl_Type objects, even the base class. If this is not an Fl_Object item, then the "o" pointer is zero. This avoided a lot of virtual functions, but is mostly for historical reasons. Rather than RTTI, I use some tests to determine subclasses: if (o && level == 1) this is an Fl_Window_Type else if (o && isparent) this is a Fl_Group_Type else if (o) this is a Fl_Object_Type else if (!level) this is a function else this is a menu item or something (none of these implemented yet) Fl_Type::first | | NULL | ^ V | +---------+ +-----------+ | Fl_Type |------ o ---> | Fl_Window | | level=0 |<-user_data()-| | |isparent |<-+ +-----------+ +---------+ | | ^ | ^ parent first | next prev / | parent V | / V | +---------+ +-----------+ | Fl_Type |------ o ---> | Fl_Object | | level=1 | | | | |<-user_data()-| | | | +-----------+ | | +---------+ | |-factory->| Fl_Type |---- o ---->[Fl_Object] +---------+ | | template object | ^ +---------+ next prev (next,prev=NULL) V | +---------+ | Fl_Type | +---------+ | ^ V | NULL | | Fl_Type::last