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

Reply via email to