Here's a half-baked idea. I'm sure there are issues, but maybe
it's a start.
Good luck,
Stan
#ifndef CHOICE_H
#define CHOICE_H
// Choice.h - Choice
// Yet another choice widget
//
// Much of this was stolen from Fl_Input_Choice
#include "FL/Fl_Group.H"
#include "FL/Fl_Widget.H"
#include "FL/Fl.H"
#include "FL/Fl_Button.H"
#include "FL/Fl_Output.H"
#include "FL/Fl_Select_Browser.H"
#include "FL/fl_draw.H"
class Choice : public Fl_Group {
private:
// This is the button that pops up a "menu"
struct DownButton : public Fl_Button {
DownButton(int x, int y, int w, int h, char const* label = 0)
: Fl_Button(x, y, w, h, label)
{;}
void draw()
{
draw_box(FL_UP_BOX, color());
fl_color(active_r() ? labelcolor() : fl_inactive(labelcolor()));
int xc = x() + w()/2, yc = y() + h()/2;
fl_polygon(xc - 5, yc - 3, xc + 5, yc - 3, xc, yc + 3);
if(Fl::focus() == this) draw_focus();
}
};
// The "menu"
class Choices : public Fl_Double_Window {
public:
Choices(int x, int y, int w, int h)
: Fl_Double_Window(x, y, w, 200)
, browser_(new Fl_Select_Browser(0, 0, w, 200))
, selection_(0)
, selected_(false)
, selectCallback_(Fl_Widget::default_callback)
, userData_(0)
{
end();
browser_->callback(select_cb, this);
box(FL_BORDER_BOX);
labeltype(FL_NO_LABEL);
clear_border();
}
// Call when a selection is made
// Will be called as c(this, ud)
void selectCallback(Fl_Callback* c, void* ud)
{ selectCallback_ = c; userData_ = ud; }
// Access to callback
Fl_Callback* selectCallback() const
{ return selectCallback_; }
// Access to user data arg for callback
void* selectUserData() const
{ return userData_; }
// Call the select callback
void doSelectCallback()
{ selectCallback_(this, userData_); }
// Pop up at specified location in window
void popup(Fl_Window* window, int x, int y)
{ position(window->x() + x, window->y() + y); show(); Fl::grab(this); }
// Go Away
void dismiss()
{ Fl::grab(0); hide(); }
// Change size
void size(int w, int h)
{ Fl_Double_Window::size(w, h); browser_->size(w, h); }
// Return most recent selection, or null if nothing selected
char const* selection() const
{ return selection_; }
// Test whether something is selected
bool selected() const
{ return selected_; }
// Add a menu choice
void add(char const* const choice)
{ browser_->add(choice); }
private:
Fl_Select_Browser* browser_;
// Internal state
char const* selection_;
bool selected_;
// Callback for selection
Fl_Callback* selectCallback_;
void* userData_;
// Make a selection
void select()
{
int v = browser_->value();
selected_ = v ? true : false;
selection_ = selected_ ? browser_->text(v) : 0;
dismiss();
doSelectCallback();
}
static void select_cb(Fl_Widget* w, void* self)
{ Choices* ch = static_cast<Choices*>(self); ch->select(); }
int handle(int event)
{
if(event == FL_PUSH && !Fl::event_inside(browser_)) {
dismiss(); return 1;
}
return Fl_Double_Window::handle(event);
}
// Not Implemented. No Copies
Choices(Choices const&);
Choices& operator=(Choices const&);
};
public:
Choice(int x, int y, int w, int h, char const* label)
: Fl_Group(x, y, w, h, label)
{
Fl_Group::box(FL_DOWN_BOX);
display_ = new Fl_Output(disp_x(), disp_y(), disp_w(), disp_h());
btn_ = new DownButton(btn_x(), btn_y(), btn_w(), btn_h());
end();
align(FL_ALIGN_LEFT);
btn_->box(FL_FLAT_BOX);
display_->box(FL_FLAT_BOX);
btn_->callback(btn_cb, this);
Fl_Group* curr = Fl_Group::current();
Fl_Group::current(0);
choices_ = new Choices(window()->x() + x, window()->y() + y, w, h);
Fl_Group::current(curr);
}
~Choice()
{ delete choices_; }
// "Menu" selection, may be null
char const* value() const
{ return display_->value(); }
// Add a "menu choice"
void add(char const* const choice)
{ choices_->add(choice); }
// Change menu size. Default is w x 300
void menu_size(int w, int h)
{ choices_->size(w, h); }
// Forwarding functions omittted for brevity. Most
// of the Fl_Widget interface should be forwarded to display_
private:
Fl_Output* display_;
DownButton* btn_;
Choices* choices_;
void value(char const* const txt)
{ display_->value(txt); }
// Callbacks
static void btn_cb(Fl_Widget*, void* self)
{ Choice* ch = static_cast<Choice*>(self); ch->btn(); }
static void select_cb(Fl_Widget*, void* self)
{ Choice* ch = static_cast<Choice*>(self); ch->select(); }
void select()
{
value(choices_->selected() ? choices_->selection() : 0);
set_changed();
}
void btn()
{
choices_->selectCallback(select_cb, this);
choices_->popup(window(), btn_->x(), y() + h());
}
// Custom resize behavior -- input stretches, menu button doesn't
// stolen from Fl_Input_Choice
int disp_x() { return(x() + Fl::box_dx(box())); }
int disp_y() { return(y() + Fl::box_dy(box())); }
int disp_w() { return(w() - Fl::box_dw(box()) - 20); }
int disp_h() { return(h() - Fl::box_dh(box())); }
int btn_x() { return(x() + w() - 20 - Fl::box_dx(box())); }
int btn_y() { return(y() + Fl::box_dy(box())); }
int btn_w() { return(20); }
int btn_h() { return(h() - Fl::box_dh(box())); }
void resize(int x, int y, int w, int h)
{
Fl_Group::resize(x, y, w, h);
display_->resize(disp_x(), disp_y(), disp_w(), disp_h());
btn_->resize(btn_x(), btn_y(), btn_w(), btn_h());
}
};
#endif
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk