Using FLUID

Say you want a UI for a C++ class called MyClass. Here's a good way to use fluid (the FLTK User Interface Designer) to do it.


1. Make a fluid file called MyClassUI.fl

From the fluid menus, add:

new > code > widget class C++ Tab > Name: MyClassUI_ new > code > decl > static const int WW=200, HH=100; // For example. new > code > decl > virtual void sync(Fl_Widget*o) {} WW and HH are copied from GUI Tab > Width & Height (you have to change WW and HH whenever you change the widget's size).

sync(Fl_Widget*) is where we'll catch subwidget callbacks.

After you've done this, you can cut-and-paste it as a template to make more widget classes.

Use fluid to build the UI.

Give each valuator a name (on the C++ Tab) like xUI for controlling an instance variable x_.

Then, select all the widgets and add:

C++ Tab > Callback: sync(o);

2. Include the fluid-generated source in your files.

In header file:

#include "MyClass.h" // Wherever you defined MyClass. #include "MyClassUI.h" // What the FLUID file made. class MyClassUI: public MyClassUI_ { // Note: fluid class name has underscore. public: MyClassUI (MyClass*); private: MyClass*obj; virtual void sync(Fl_Widget*); };

In implementation file:

MyClassUI::MyClassUI(MyClass*X): MyClassUI_(0, 0, WW, HH, "MyClassUI") { obj = X; sync(0); // Init UI widgets to MyClass values. } /* Assume MyClass has accessors for internal variables. */ // Subwidget callback manager. void MyClassUI::sync(Fl_Widget*o) { if (!obj) return; // Put UI values in class. if (o==xUI) { obj->x( xUI->value() ); } if (o==yUI) { obj->y( yUI->value() ); } // Put class values in UI. xUI->value( obj->x() ); yUI->value( obj->y() ); // Inform parent of changes. if(o) { do_callback(); } //(if callback installed) } The callback logic is all in one place-- MyClassUI::sync()-- instead of hidden in fluid's tiny little boxes.

You get to design the interface in fluid, and best of all, the fluid files have no dependencies and will always compile.


3. Install a callback so the rest of your program knows when the UI is used.

Example:

Here's a top-level window, Panel, that could have many widget classes displayed within.

It has it's own sync() function to catch callbacks from it's sub-widgets.

class Panel: public Fl_Window { public: Panel(int W, int H); private: static inline void CB(Fl_Widget*o, void*v) {((Panel*)v)->sync(o);} void sync(Fl_Widget*); MyClass obj; MyClassUI ui; }; Panel::Panel(int W, int H): Fl_Window(W, H, "Panel"), obj(), ui(&obj) { end(); ui.callback(CB, this); } void Panel::sync(Fl_Widget*o) { if (o==&ui) do_something(); }



4. A test program

If MyClass is itself a widget, such as a graphics view, you'll need to create an additional Fl_Window to hold it.

If it's not a widget, just an implementation of an algorithm, this works:

#include <FL/Fl_Window.H> #include <FL/Fl.H> #include "MyClassUI.h" // Wherever you defined MyClassUI. int main(int argc, char**argv) { Fl_Window winUI(MyClassUI::WW, MyClassUI::HH, "MyClassUI"); MyClass obj; MyClassUI ui(&obj); winUI.end(); winUI.resizable(&ui); winUI.show(); return Fl::run(); }



NR 2006.