Ian wrote:
> Ah, but there's a thought... If Denton has already modified his widgets =
> to handle the focus differently, then they may perhaps be "eating" the =
> nav events, thus preventing them from propagating to the container =
> scroll widget at all?
>
I may have done that, yes. Have to go back and check now!
For both of you (Ian, Albrecht), here is my solution. It includes a test
sequence that produces some random buttons, and lets the client dynamically
change the size of the scrollbar. I wanted to allow a dynamic box style change
for the scroll group, but it didn't work for some reason ... it just wouldn't
redraw in the new style (???). I'm on 1.3.0 - I think I should just update to
the newest release.
Thanks in advance for any advice - this will certainly be helpful in my current
project.
Denton
---
/** \file Track_Focus_Scroll-Main.cpp
Testing/Draft: Track_Focus_Scroll
CHANGES
- dentonlt, january 2013: original version
- kudos to Albrecht Schlosser and Ian MacArthur for their assistance;
see the fltk newsgroup for replies to:
http://fltk.org/newsgroups.php?gfltk.general+v:35850
*/
// stdlib
#include <cstdio>
// FLTK lib
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Int_Input.H>
class Track_Focus_Scroll;
Fl_Window *win_;
Fl_Int_Input *f; // fudge input
Fl_Int_Input * s; // scrollbar width input
Track_Focus_Scroll *scroll_;
/** @class Track_Focus_Scroll
\brief Fl_Scroll that auto-scrolls to widget in focus
This subclass of Fl_Scroll provides a handle() function that puts
the Fl::focus() widget in view whenever the focus changes. handle()
checks for a focus change after either FL_KEYBOARD or FL_PUSH.
\see handle()
\see scroll_to_focus()
*/
class Track_Focus_Scroll : public Fl_Scroll {
Fl_Widget *focus_tracker_; ///< for tracking the Fl::focus() widget
/** if Fl_Scroll:handle() manages event && Fl::focus() changes, call
adjust_scroll_() to manage scroll bars */
int handle(int event) {
int ret = Fl_Scroll::handle(event);
if((event == FL_KEYBOARD) || (event == FL_PUSH)) {
if(ret && scroll_to_focus_()) redraw();
}
return ret;
}
/** This is a support function for scroll_to_focus_(). You should not have to
call this function directly.
\see scroll_to_focus_()
test Fl::focus() to see (1) if focus has changed since last call
and (2) if focus is inside Fl_Scroll. If both true, return 1. Else return 0.
*/
int test_focus_() {
// if widget is not a widget inside of the scroll group, end.
if(focus_tracker_ == Fl::focus()) return 0;
// record new focus ...
if(focus_tracker_) focus_tracker_->color(FL_BLUE);
focus_tracker_ = Fl::focus();
focus_tracker_->color(FL_RED);
if((!focus_tracker_->inside(this)) ||
(focus_tracker_ == &scrollbar) || (focus_tracker_ == &hscrollbar) ||
focus_tracker_->inside(&scrollbar) ||
focus_tracker_->inside(&hscrollbar))
return 0;
return 1; // scroll to it!
}
/** This is a support function for scroll_to_focus_(). You should not have to
call this function directly.
\see scroll_to_focus_()
test focus_tracker_'s x dimensions. If all/part of widget is outside view
area,
calculate a new scroll xposition that will put focus_tracker_ in the view
area.
Return that new scoll xposition. */
int scroll_to_focus_x_() {
int xpos = xposition();
if(!focus_tracker_) return xpos; // avoid glorious segfault ...
int sx, sy, sw, sh;
bbox(sx, sy, sw, sh); // visible scroll area dimensions ...
int style_fudge_ = x() - sx; // to remove the few pixels taken up by the
style border
int wx = focus_tracker_->x();
int ww = focus_tracker_->w();
int minw = (ww < sw) ? ww : sw; // get smaller of widget width or scroll
view width
//otherwise, we scroll until as much as possible of the focus_tracker_
widget is in view.
if(wx > sx + sw - ww) // scroll right
return xpos + wx - sw + minw - x() + style_fudge_; // move ahead by (dist
b/t window edge & widget) + minw + fudge
if(wx < sx) // scroll left; this should always scroll until the widget's
left side is against the scroll field left side.
return xpos + wx - x() + style_fudge_; // move back by (dist b/t window
edge and widget) + fudge
return xpos;
}
/** This is a support function for scroll_to_focus_(). You should not have to
call this function directly.
\see scroll_to_focus_()
test focus_tracker_'s y dimensions. If all/part of widget is outside view
area,
calculate a new scroll yposition that will put focus_tracker_ in the view
area.
Return that new scoll yposition. */
int scroll_to_focus_y_() {
int ypos = yposition(); // get current scroll position
if(!focus_tracker_) return ypos; // avoid glorious segfault ...
int sx, sy, sw, sh;
bbox(sx, sy, sw, sh); // get dimensions ...
int style_fudge_ = y() - sy;
int wy = focus_tracker_->y();
int wh = focus_tracker_->h();
int minh = (wh < sh) ? wh : sh; // get smaller of widget height or scroll
view height
//scroll until as much as possible of the focus_tracker_ widget is in view.
if(wy > sh - wh) // scroll down
return ypos + wy - sh + minh - y() + style_fudge_;
if(wy < 0) // scroll up
return ypos - (y() - wy) + style_fudge_;
return ypos; // no change
}
/** scroll_to_focus_() checks focus_widget_() to see if it is in view. If it
isn't,
scroll_to_focus_() will adjust scroll position so that prev_focus_ is in
view.
scroll_to_focus_() returns 1 if it changes the scroll position, 0 if it
does not.
\see test_focus_()
\see scroll_to_focus_x_()
\see scroll_to_focus_y_()
*/
int scroll_to_focus_() {
// check if we need to update/scroll. If not, return 0.
if(!test_focus_()) return 0;
// there may be a faster way to find the scroll coords, but ...
int xpos = scroll_to_focus_x_();
int ypos = scroll_to_focus_y_();
//scroll to widet
scroll_to(xpos, ypos);
return 1; // tell handle() we need to request redraw()
}
public:
/// constructor sets focus_tracker_ to NULL and calls inherited constructor
Track_Focus_Scroll(int X, int Y, int W, int H, const char *name) :
Fl_Scroll(X, Y, W, H, name) {
focus_tracker_ = NULL;
}
virtual ~Track_Focus_Scroll() {}; ///< empty destructor, virtual
};
void set_scrollbar_size_cb_(Fl_Widget *src, void *dat) {
int x = atoi(((Fl_Int_Input *)src)->value());
Fl::scrollbar_size(x);
printf("Set scrollbar size to %d\n", x);
}
void setup() {
win_ = new Fl_Window (500, 300);
scroll_ = new Track_Focus_Scroll(10, 30, 400, 250, "scroll group");
scroll_->labelsize(9);
scroll_->color(FL_GREEN);
scroll_->box(FL_FLAT_BOX);
for(int i = 0; i < 32; i++) {
int ypos = rand() % 5000;
int xpos = rand() % 5000;
int ww = rand() % 500; if (ww < 50) ww = 50;
int wh = rand() % 500; if (wh < 50) wh = 50;
int boxtype = rand() % 4;
Fl_Button *b = new Fl_Button(xpos, ypos, ww, wh);
b->labelsize(9);
switch(boxtype) {
case(0): b->box(FL_PLASTIC_UP_BOX);
break;
;;
case(1): b->box(FL_THIN_DOWN_BOX);
break;
;;
case(2): b->box(FL_GTK_UP_BOX);
break;
;;
case(3): b->box(FL_FLAT_BOX);
break;
;;
} // end switch boxtype
b->color(FL_BLUE);
// set label as widget coord
char buf[32];
memset(buf, 0, sizeof(buf));
sprintf(buf, "%d, %d [%d]", xpos, ypos, boxtype);
b->copy_label(buf);
}
scroll_->end();
s = new Fl_Int_Input(scroll_->x() + scroll_->w() + 10, scroll_->y(), 50, 35,
"scrollbar size");
s->labelsize(9);
s->align(FL_ALIGN_TOP);
s->callback(set_scrollbar_size_cb_, NULL);
win_->resizable(scroll_);
win_->end();
win_->show();
}
int main(void)
{
setup(); // alloc widgets, attach callback to Fl_Int_Input
return(Fl::run());
delete win_;
}
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk