GIDNetwork > Derived FLTK Classes
Register
« New payment service from Google? Who likes to get a line wet? »

Derived FLTK Classes

by: cable_guy_67 - Jun 27, 2005

A FLTK Phone Book UI

I have been playing with some ideas to compartmentalize my FLTK applications and a member at GIDForums asked about how to extend your own widget classes. So I, in my over the top way, designed a UI that will work with a infinite array Phone Book I wrote. The PBook is not implemented yet but the UI is designed. All that is left is to create the remaining callbacks and interface it with the PBook class.

There are a lot of little things that may be of interest.

  1. A simple how to for deriving classes and using those classes as original fltk types.

  2. In class callbacks.

  3. Exiting without calling exit().

  4. Reusable custom widgets.

  5. Flicker protector group.

  6. Menu array pointers.

Now, that being said, this is not even vaguely complete. The phone book portion is still in console form and it was rudementary at best. It is based on a self extending concept that builds a page of objects at a time and uses an overloaded operator[] for indexing. Currently it is a singly linked list. To be useful it will need to become doubly linked and have a current pointer added.

The idea is to have the UI have two views. A single entry and a page of entries that matches the paging system of the PBook database. It should have simple sorting based on one of the fields in the record. Nothing ground-breaking but it seemed both simple and interesting enough for the real project, which was trying to get reusability and ease of creation for FLTK widgets. Simple things but hopefully very useful in the long run. It will also serve to give someone starting with FLTK some room to improve on their own.

Now, I'm not exactly a professional at this so feel free to point out ways that this could be done better. One thing that concerns me is the data base object. It seems to make sense that it would be seperately maintained from the UI. From within the UI I can always find my way back to the main window (see the exit menu callback) but I am not entirely sure how I will handle the database itself. Working within FLTK's event loop still gives me a bit of a headache sometimes. One of the nice things about it though is that FLTK works with C++ code and has a very straightforward method of get/put using overloading the return type which I use in the phone book portion of the code.

Well, that's about enough for an introduction. In order to build this you will need to have FLTK 1.1.x installed and use the command line:

Generic Code Example:

g++ -Wall fltkPBook_UI.cpp PBookMenu.cpp PBookGroups.cpp MyWidgets.cpp `fltk-config --ldflags --cxxflags` -s -o fltkPBook

I have built it with the current subversion repo on Windows 2000 using the CygWin GNU tools. It should be portable especially in it's current state as I try to use the C++ standard and no STL. I do like to use <string> though so that is the only place I can think of being a problem. When I get to it I will post the working version of the project. If anyone wants to pitch in, feel free. Any OOP comentary will be most welcome as well. Here is what it currently looks like.

Enjoy.

Mark

fltkPBook_UI.cpp

C/CPP/C++ Code Example:

///////////////fltkPBook_UI.cpp///////////////////

#include "fltkPBook_UI.h"

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Enumerations.H>

PBookUI::PBookUI(const char* L) : Fl_Double_Window(0, 0, 340, 160, L) {

  free_position();
  callback(cb_exit,this);
  color(FL_BACKGROUND_COLOR);
  selection_color(FL_BACKGROUND_COLOR);
  labelfont(0);
  align(FL_ALIGN_TOP);

  add(menu_bar);
  add(entry_group);
  add(butbar_group);
  add(nav_group);

  end();
}


// BELOW here should become part of its own .cpp file
int main(int argc, char* argv[]){

  Fl::visual(FL_DOUBLE|FL_INDEX);
  PBookAPP PBook;
  PBook.Show(argc, argv);
  return(Fl::run());
}

fltkPBook_UI.h

C/CPP/C++ Code Example:

///////////////fltkPBook_UI.h///////////////////

#if !defined __FLTKPBOOK_UI_H__
#define __FLTKPBOOK_UI_H__

#include "PBookGroups.h"
#include "PBookMenu.h"
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Widget.H>
#include <FL/Enumerations.H>

class PBookUI : public Fl_Double_Window {

  public:
    PBookUI(const char *L = "PBook");
    ~PBookUI(){};

  private:
    MenuMB   menu_bar;
    EntryGP  entry_group;
    ButBarGP butbar_group;
    NavGP    nav_group;
    void icb_exit() { hide(); };
    static void cb_exit(Fl_Widget*, void* user)
      { ((PBookUI*)user)->icb_exit(); };
};

class PBookAPP {
  public:
    PBookAPP() : app_window("Test Book"){};
    ~PBookAPP(){};
    void Show(int argc, char* argv[]){app_window.show(argc, argv);};

  private:
//    IAPBook phone_book;
    PBookUI app_window;
};

#endif

PBookMenu.cpp

C/CPP/C++ Code Example:

///////////////PBookMenu.cpp///////////////////

#include "PBookMenu.h"

#include <FL/Fl.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Widget.H>
#include <FL/fl_ask.H>
#include <FL/Enumerations.H>

MenuMB::MenuMB() : Fl_Menu_Bar(0, 0, 340, 20) {

    new_menu->callback(cb_new, this);
    load_menu->callback(cb_load, this );
    save_menu->callback(cb_save, this );
    exit_menu->callback(cb_exit, this );
    about_pbook->callback(cb_about,(void*)PBOOK);
    about_fltk->callback(cb_about,(void*)FLTK);
    about_free->callback(cb_about,(void*)FREE);
    
    copy(menu_main);
}

Fl_Menu_Item MenuMB::menu_main[] = {
  {"File", 0, 0, menu_file, FL_SUBMENU_POINTER, FL_NORMAL_LABEL, 0, 14, 56},
  {"About", FL_ALT+'a', 0, menu_about, FL_SUBMENU_POINTER, FL_NORMAL_LABEL, 0, 14, 56},
  {0,0,0,0,0,0,0,0,0}
};

Fl_Menu_Item MenuMB::menu_file[] = {
  {"&New", FL_ALT+'n', 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {"&Load", FL_ALT+'l', 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {"&Save", FL_ALT+'s', 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {"&Exit", FL_ALT+'e', 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {0,0,0,0,0,0,0,0,0}
};

Fl_Menu_Item* MenuMB::new_menu = MenuMB::menu_file + 0;
Fl_Menu_Item* MenuMB::load_menu = MenuMB::menu_file + 1;
Fl_Menu_Item* MenuMB::save_menu = MenuMB::menu_file + 2;
Fl_Menu_Item* MenuMB::exit_menu = MenuMB::menu_file + 3;

Fl_Menu_Item MenuMB::menu_about[] = {
  {"PBook", 0, 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {"FLTK", 0, 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {"Freedom", 0, 0, 0, 0, FL_NORMAL_LABEL, 0, 14, 56},
  {0,0,0,0,0,0,0,0,0}
};

Fl_Menu_Item* MenuMB::about_pbook = MenuMB::menu_about + 0;
Fl_Menu_Item* MenuMB::about_fltk = MenuMB::menu_about + 1;
Fl_Menu_Item* MenuMB::about_free = MenuMB::menu_about + 2;

void MenuMB::cb_about(Fl_Widget*, void* user) {

  switch ( (int)user ) {
    case PBOOK:
      fl_message( "PBook started when I was playing with overloading the\n"
                  "operator[] and some phone book posts at GIDForums.\n"
                  "It will be a base for further specilized group classes.\n"
                  "To build from source you will need the fltk library\n"
                  "installed. It can be found at www.fltk.org/software.php." );
      break;
    case FLTK:
      fl_message( "The Fast Light Tool Kit is a free (the real good kind)\n"
                  "UI tool kit that makes cross platform development\n"
                  "very easy to accomplish. It is licienced under the LGPL.\n"
                  "For more informantion visit www.fltk.org" );
      break;
    case FREE:
      fl_message( "One of the things I have found as I learn to use these\n"
                  "tools is the amount of freedom they afford. Both in what\n"
                  "is possible to create and what you can do with the final\n"
                  "product. Using GCC and FLTK (well CygWin isn't REALLY free\n"
                  "but hey...) has opened my eyes to what FREE really means.\n"
                  "It is the freedom to do as you desire and allow others the\n"
                  "same chance. It means you can find and learn from\n"
                  "professional examples just because of the GPL or LGPL.\n"
                  "There is power in that kind of freedom. Remember to support\n"
                  "these types of authors so we can continue to keep Freedom Free.\n" );
      break;
  }
}

PBookMenu.h

C/CPP/C++ Code Example:

///////////////PBookMenu.h///////////////////

#if !defined __PBOOKMENU_H__
#define __PBOOKMENU_H__

#include <FL/Fl.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Double_Window.H>

class MenuMB : public Fl_Menu_Bar {

  public:
    MenuMB();
    ~MenuMB(){};

  private:
    enum {PBOOK = 0, FLTK, FREE};

    void icb_new(){};
    static void cb_new(Fl_Widget*, void* user)
      { ((MenuMB*)user)->icb_new(); };
    void icb_load(){};
    static void cb_load(Fl_Widget*, void* user)
      { ((MenuMB*)user)->icb_load(); };
    void icb_save(){};
    static void cb_save(Fl_Widget*, void* user)
      { ((MenuMB*)user)->icb_save(); };
    void icb_exit(Fl_Widget* w) {
      w->window()->hide();
    };
    static void cb_exit(Fl_Widget* w, void* user)
      { ((MenuMB*)user)->icb_exit(w); };
    static void cb_about(Fl_Widget*, void* user);

    static Fl_Menu_Item menu_main[];
    static Fl_Menu_Item menu_file[];
    static Fl_Menu_Item menu_about[];
      static Fl_Menu_Item* new_menu;
      static Fl_Menu_Item* load_menu;
      static Fl_Menu_Item* save_menu;
      static Fl_Menu_Item* exit_menu;
      static Fl_Menu_Item* about_pbook;
      static Fl_Menu_Item* about_fltk;
      static Fl_Menu_Item* about_free;
};

#endif

PBookGroups.cpp

C/CPP/C++ Code Example:

///////////////PBookGroups.cpp///////////////////

#include "PBookGroups.h"
#include <FL/Fl.H>
#include <FL/Fl_Group.H>
#include <FL/Enumerations.H>

EntryGP::EntryGP() : Fl_Group(0, 20, 340, 100),
                     name_box(10, 30, 70, 20, "Name :"),
                   number_box(10, 60, 70, 20, "Number :"),
                    where_box(10, 90, 70, 20, "Where :"),
                     name_out(90, 30, 240, 20),
                   number_out(90, 60, 240, 20),
                    where_out(90, 90, 240, 20),
             entry_hide_group(x(), y(), w(), h()) {

  box(FL_ENGRAVED_FRAME);
  when(FL_WHEN_RELEASE);
  
  add(name_box);
  add(number_box);
  add(where_box);
  add(name_out);
  add(number_out);
  add(where_out);
  add(entry_hide_group);

  end();
}


ButBarGP::ButBarGP() : Fl_Group(0, 120, 340, 20),
                    edit_button(0, y(), 85, h(), "Edit"),
                    page_button(85, y(), 85, h(), "page view"),
                  cancel_button(170, y(), 85, h(), "cancel"),
                  modify_button(255, y(), 85, h(), "modify") {

  box(FL_ENGRAVED_FRAME);
  when(FL_WHEN_RELEASE);

  add(edit_button);
  add(page_button);
  add(page_button);
  add(modify_button);

  end();
}

NavGP::NavGP() : Fl_Group(0, 140, 340, 20), 
                 name_button(0, y(), 70, h(), "Name"),
                 number_button(70, y(), 70, h(), "Number"),
                 where_button(140, y(), 70, h(), "Where"),
                 back_button(210, y(), 65, h(), "@<-"),
                 next_button(275, y(), 65, h(), "@->") {

  when(FL_WHEN_RELEASE);
  box(FL_ENGRAVED_FRAME);

  // These are a radio group
  add(name_button);
  add(number_button);
  add(where_button);
  // Pick a default on
  name_button.setonly();
  
  add(back_button);
  add(next_button);

  end();
}

PBookGroups.h

C/CPP/C++ Code Example:

///////////////PBookGroups.h///////////////////

#if !defined __PBOOKGROUPS_H__
#define __PBOOKGROUPS_H__

#include "MyWidgets.h"
#include <FL/Fl.H>
#include <FL/Fl_Group.H>


class EntryGP : public Fl_Group {

  public:
    EntryGP();
    ~EntryGP(){};

  private:
    StandardLBL name_box;
    StandardLBL number_box;
    StandardLBL where_box;
    StandardOUT name_out;
    StandardOUT number_out;
    StandardOUT where_out;
    ScreenGP    entry_hide_group;
};


class ButBarGP : public Fl_Group {

  public:
    ButBarGP();
    ~ButBarGP(){};

  private:
    LightImpBUT edit_button;
    StandardBUT page_button;
    StandardBUT cancel_button;
    ReturnBUT   modify_button;
};


class NavGP : public Fl_Group {

  public:
    NavGP();
    ~NavGP(){};

  private:
    LightRadioBUT name_button;
    LightRadioBUT number_button;
    LightRadioBUT where_button;
    StandardBUT   back_button;
    StandardBUT   next_button;
};

#endif

MyWidgets.cpp

C/CPP/C++ Code Example:

///////////////MyWidgets.cpp///////////////////

#include "MyWidgets.h"

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <Fl/fl_draw.H>
#include <FL/Enumerations.H>

LightRadioBUT::LightRadioBUT(int X, int Y, int W, int H, const char* L)
           : Fl_Light_Button(X, Y, W, H, L) {

  type(FL_RADIO_BUTTON);
  box(FL_THIN_UP_BOX);
  selection_color(fl_darker(FL_GREEN));
  labeltype(FL_ENGRAVED_LABEL);
  labelcolor(fl_darker(FL_BLUE));
  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
  when(FL_WHEN_RELEASE);
}


LightImpBUT::LightImpBUT(int X, int Y, int W, int H, const char* L)
           : Fl_Light_Button(X, Y, W, H, L) {

  box(FL_THIN_UP_BOX);
  selection_color(FL_RED);
  labeltype(FL_ENGRAVED_LABEL);
  labelcolor(fl_darker(FL_BLUE));
  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
  when(FL_WHEN_RELEASE);
}


StandardBUT::StandardBUT(int X, int Y, int W, int H, const char* L)
              : Fl_Button(X, Y, W, H, L) {

  box(FL_THIN_UP_BOX);
  down_box(FL_THIN_DOWN_BOX);
  labeltype(FL_ENGRAVED_LABEL);
  labelcolor(fl_darker(FL_BLUE));
  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
  when(FL_WHEN_RELEASE);
}


ReturnBUT::ReturnBUT(int X, int Y, int W, int H, const char* L)
              : Fl_Return_Button(X, Y, W, H, L) {

  box(FL_THIN_UP_BOX);
  down_box(FL_THIN_DOWN_BOX);
  labeltype(FL_ENGRAVED_LABEL);
  labelcolor(fl_darker(FL_BLUE));
  align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
  when(FL_WHEN_RELEASE);
}


int ReturnBUT::fl_return_arrow(int x, int y, int w, int h) {
  int size = w; if (h<size) size = h;
  int d = (size+2)/4; if (d<3) d = 3;
  int t = (size+9)/12; if (t<1) t = 1;
  int x0 = x+(w-2*d-2*t-1)/2;
  int x1 = x0+d;
  int y0 = y+h/2;
  
  fl_color(fl_darker(FL_BLUE));
  fl_begin_polygon();
    fl_vertex(x0, y0);
    fl_vertex(x1, y0+d);
    fl_vertex(x1, y0+t);
    fl_vertex(x1+d+2*t, y0+t);
    fl_vertex(x1+d+2*t, y0-d);
    fl_vertex(x1+d, y0-d);
    fl_vertex(x1+d, y0-t);
    fl_vertex(x1+1, y0-t);
    fl_vertex(x1, y0-t);
    fl_vertex(x1, y0-d);
    fl_vertex(x0, y0);
  fl_end_polygon();

  fl_color(fl_lighter(FL_BLUE));
  fl_line(x0, y0, x1, y0+d);
  fl_yxline(x1, y0+d, y0+t, x1+d+2*t,y0-d);
  fl_color(fl_darker(FL_BLUE));
  fl_xyline(x1+d+2*t,y0-d, x1+d, y0-t, x1+1);
  fl_color(fl_lighter(FL_BLUE));
  fl_line(x1, y0-t, x1, y0-d);
  fl_color(fl_darker(FL_BLUE));
  fl_line(x1, y0-d, x0, y0);

  return 1;
}

void ReturnBUT::draw() {
  if (type() == FL_HIDDEN_BUTTON) return;
  draw_box(value() ? (down_box()?down_box():fl_down(box())) : box(),
	   value() ? selection_color() : color());
  int W = h();
  if (w()/3 < W) W = w()/3;
  fl_return_arrow(x()+w()-W-4, y(), W, h());
  draw_label(x(), y(), w()-W+4, h());
  if (Fl::focus() == this) draw_focus();
}


StandardLBL::StandardLBL(int X, int Y, int W, int H, const char* L)
              : Fl_Box(X, Y, W, H, L) {

  labeltype(FL_EMBOSSED_LABEL);
  labelcolor(FL_BLUE);
  align(FL_ALIGN_RIGHT|FL_ALIGN_INSIDE);
  when(FL_WHEN_NEVER);
}


StandardOUT::StandardOUT(int X, int Y, int W, int H)
             : Fl_Output(X, Y, W, H) {

  labeltype(FL_NO_LABEL);
  when(FL_WHEN_NEVER);
}


ScreenGP::ScreenGP(int X, int Y, int W, int H)
             : Fl_Group(X, Y, W, H) {

  box(FL_NO_BOX);
  labeltype(FL_NO_LABEL);
  when(FL_WHEN_NEVER);
  deactivate();
  
  end();
}

MyWidgets.h

C/CPP/C++ Code Example:

///////////////MyWidgets.h///////////////////

#if !defined __MYWIDGETS_H__
#define __MYWIDGETS_H__

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Group.H>

class LightRadioBUT : public Fl_Light_Button {

  public:
    LightRadioBUT(int X, int Y, int W, int H, const char* L);
    ~LightRadioBUT(){};
};


class LightImpBUT : public Fl_Light_Button {

  public:
    LightImpBUT(int X, int Y, int W, int H, const char* L);
    ~LightImpBUT(){};
};


class StandardBUT : public Fl_Button {

  public:
    StandardBUT(int X, int Y, int W, int H, const char* L);
    ~StandardBUT(){};
};


class ReturnBUT : public Fl_Return_Button {

  public:
    ReturnBUT(int X, int Y, int W, int H, const char* L);
    ~ReturnBUT(){};
  
  private:
    int fl_return_arrow(int x, int y, int w, int h);
    void draw();
};


class StandardLBL : public Fl_Box {

  public:
    StandardLBL(int X, int Y, int W, int H, const char* L);
    ~StandardLBL(){};
};


class StandardOUT : public Fl_Output {

  public:
    StandardOUT(int X, int Y, int W, int H);
    ~StandardOUT(){};
};


class ScreenGP : public Fl_Group {

  public:
    ScreenGP(int X, int Y, int W, int H);
    ~ScreenGP(){};
};

#endif
Would you like to comment? This story has been viewed 9,199 times.
« New payment service from Google? Who likes to get a line wet? »

__top__

Copyright © GIDNetwork™ 2001 - 2017

Another website by J de Silva

Page generated in : 0.02651 sec.