Hello,

I am currently struggling with a problem which is somewhat related to
popup menus.
What I would like to achieve is the same behavior as a popup menu but
with arbitrary widgets.

The behavior should be
1) (the simple part - straight forward): click + release on some button
- then the dialog pops up and you can click on any of the widgets in it.
They all respond to FL_ENTER, FL_LEAVE, FL_PUSH and FL_RELEASE. Once a
widget has been pushed the dialog disappears again. Also a click outside
the dialog hides it again.

2) (the harder part - not working): click -> dialog pops up while the
mouse is still pressed and receives FL_DRAG. Messages are routed to the
popup windows as if the mouse is not pushed, i.e. the children receive
FL_ENTER, FL_LEAVE, FL_PUSH/FL_RELEASE (=actually FL_RELEASE on
menubutton) and react accordingly (redraw etc.).
If the mouse is released outside the dialog then it is hidden again.

I have tried several methods including:

1) call Fl::handle in button-handle with translated messages
(FL_DRAG->FL_MOVE, FL_RELEASE->FL_PUSH) -> crash
2) same but with Fl::add_timeout(0,..) + Fl::handle -> not working
3) call handle of the window directly -> crash
4) Fl::grab + Fl::wait -> not working

resulting mostly in either non working behavior or worse crashes. I'm
sure there is a simple way to achieve this, as so often in FLTK, I
simply haven't found it yet.

My testcode is below. There are four test cases, which you can uncomment
- needless to say, none of them works :-(

// M E N U T E S T . C X ///////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>


// global for test case 4: grab while this variable is true
int g_popup=1;

class Fl_Active_Button:public Fl_Button
{
  public:
  Fl_Active_Button(int X, int Y, int W, int H, const char*
L=""):Fl_Button(X,Y,W,H,L)
  { color(FL_BLUE);
  }

  int handle(int e)
  { fprintf(stderr,"receive event in active button: %d\n",e);
    switch(e)
    { case FL_ENTER: color(FL_RED); redraw(); return(1);
      case FL_LEAVE: color(FL_BLUE); redraw(); return(1);
      case FL_PUSH:
      case FL_RELEASE: g_popup=0; return(1);
      default:  return(Fl_Button::handle(e));
    }
  }
};

// test cases: uncomment different scenarios
//
// test 1: send messages in handle with Fl::handle to redirect-window
//         (uncomment in FL_RELEASE and FL_DRAG)
// test 2: call postsend (to send a message with Fl::add_timeout and
//         Fl::handle)
//         (uncomment in FL_RELEASE and FL_DRAG)
// test 3: call handle function of redirect window directly
//         (uncomment in FL_RELEASE and FL_DRAG)
// test 4: use grab and and Fl::wait in FL_PUSH
//         (uncomment in FL_PUSH)

class Fl_Redirect_Button:public Fl_Button
{ Fl_Window* _win;
  int _msg;
  public:
  Fl_Redirect_Button(int X, int Y, int W, int H, const char*
L=""):Fl_Button(X,Y,W,H,L)
  { redirect(NULL);
    _msg=0;
  }

  void redirect(Fl_Window* w) {_win=w;}
  Fl_Window* redirect() {return(_win);}

  static void s_sendmsg(void*o){ ((Fl_Redirect_Button*)o)->sendmsg(); }
  void sendmsg() { if (redirect()) Fl::handle(_msg,redirect()); }
  void postsend(int msg)
  { _msg=msg; Fl::add_timeout(0.0,s_sendmsg,this);}

  int handle(int e)
  { switch(e)
    { case FL_PUSH:
      { if (redirect())
        { redirect()->position(window()->x()+x(),window()->y()+y()+h());
          redirect()->show();
          redirect()->set_modal();

          // method 4 - Fl::grab - does not work
          // Fl::grab(redirect());
          // Fl::focus( redirect() );
          // Fl::pushed( redirect() );
          // g_popup=1;
          // while( g_popup) Fl::wait();
          // fprintf(stderr,"grab out\n");
          // Fl::grab(0);
          // redirect()->hide();
          // Fl::focus(this);

          return(1);
        }
      } break;

      case FL_RELEASE:
      { if (redirect() && Fl::event_inside(redirect()))
        { // method 1
          // Fl::handle(FL_PUSH,redirect());

          // method 2
          // postsend(FL_PUSH);

          // method 3
          // return(redirect()->handle(e));

          redirect()->hide();
          return(1);
        }
        else
        { redirect()->hide();
        }
      } break;

      case FL_DRAG:
      { if (redirect())
        {
          // method 1: direct call in handle of Fl::handle => crash
          // Fl::handle(FL_MOVE,redirect());

          // method 2 - call it with a timeout(0.0)
          // postsend(FL_MOVE);

          // method 3 - call handle of widget directly => crash
          // return(redirect()->handle(e));
          return(1);
        }
      } break;
    }
    return(Fl_Button::handle(e));
  }
};

int main(int argc, char** argv)
{
  Fl_Window* hidden=new Fl_Window(120,100);
      hidden->border(0);
      hidden->set_modal();
      Fl_Active_Button* ba=new Fl_Active_Button(10,10,100,50,"Hello");
  hidden->end();

  Fl_Window * win=new Fl_Window(120,100,"test menu");
      Fl_Redirect_Button* b=new Fl_Redirect_Button(10,10,100,25,"clickme");
      b->redirect(hidden);
  win->end();

  win->show(argc,argv);
  return(Fl::run());
}

Has anybody made similar attempts, or even managed to solve the problem?
Any pointers, hints or example code would be greatly appreciated.

I have found Greg's example on the popup text window, which is nice but
this is only a prerequisite and does not show how events would be routed
to the window between FL_PUSH and FL_RELEASE events.

I am aware that in FLTK2 it seems to be possible to add widgets to menus
- but backporting seems to be a major undertaking. Also the FLTK3
manifesto mentions something along these lines - however, as I
understand - it is currently on the wish list only.

I would prefer if it were possible - with whatever means - to emulate
such a behavior in FLTK 1.3.

Thanks,

Herman
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to