Fl_Scroll mods for global scrollbar size control.

Also, unittest added (scrollbar size) to test these changes.
    Thanks to Albrecht for testing/peer review.



git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6828 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Greg Ercolano 2009-07-12 00:15:06 +00:00
parent 9eaf693d4a
commit fe687baefd
3 changed files with 218 additions and 72 deletions

View File

@ -95,11 +95,33 @@ class FL_EXPORT Fl_Scroll : public Fl_Group {
int xposition_, yposition_;
int width_, height_;
int oldx, oldy;
int scrollbar_size_;
static void hscrollbar_cb(Fl_Widget*, void*);
static void scrollbar_cb(Fl_Widget*, void*);
void fix_scrollbar_order();
static void draw_clip(void*,int,int,int,int);
private:
//
// Structure to manage scrollbar and widget interior sizes.
//
// Private for now -- we'd like to expose some of this at
// some point to solve STR#1895.)
//
typedef struct {
int scrollsize; // the scrollsize (global|local)
int innerbox_x, innerbox_y, innerbox_w, innerbox_h; // widget's inner box (excludes scrollbars)
int innerchild_x, innerchild_y, innerchild_w, innerchild_h; // widget's inner box including scrollbars
int child_l, child_r, child_b, child_t; // child bounding box: left/right/bottom/top
int hneeded, vneeded; // hor + ver scrollbar visibility
int hscroll_x, hscroll_y, hscroll_w, hscroll_h; // hor scrollbar size/position
int vscroll_x, vscroll_y, vscroll_w, vscroll_h; // ver scrollbar size/position
int hpos, hsize, hfirst, htotal; // hor scrollbar values (pos/size/first/total)
int vpos, vsize, vfirst, vtotal; // ver scrollbar values (pos/size/first/total)
} ScrollInfo;
void recalc_scrollbars(ScrollInfo &si);
protected:
void bbox(int&,int&,int&,int&);
@ -131,6 +153,41 @@ public:
int yposition() const {return yposition_;}
void scroll_to(int, int);
void clear();
/**
Gets the current size of the scrollbars' troughs, in pixels.
If this value is zero (default), this widget will use the
Fl::scrollbar_size() value as the scrollbar's width.
\returns Scrollbar size in pixels, or 0 if the global Fl::scrollsize() is being used.
\see Fl::scrollbar_size(int)
*/
int scrollbar_size() const {
return(scrollbar_size_);
}
/**
Sets the pixel size of the scrollbars' troughs to the \p size, in pixels.
Normally you should not need this method, and should use
Fl::scrollbar_size(int) instead to manage the size of ALL
your widgets' scrollbars. This ensures your application
has a consistent UI, is the default behavior, and is normally
what you want.
Only use THIS method if you really need to override the global
scrollbar size. The need for this should be rare.
Setting \p size to the special value of 0 causes the widget to
track the global Fl::scrollbar_size(), which is the default.
\param[in] size Sets the scrollbar size in pixels.\n
If 0 (default), scrollbar size tracks the global Fl::scrollbar_size()
\see Fl::scrollbar_size()
*/
void scrollbar_size(int size) {
if ( size != scrollbar_size_ ) redraw();
scrollbar_size_ = size;
}
};
#endif

View File

@ -52,6 +52,9 @@ void Fl_Scroll::fix_scrollbar_order() {
}
}
// Draw widget's background and children within a specific clip region
// So widget can just redraw damaged parts.
//
void Fl_Scroll::draw_clip(void* v,int X, int Y, int W, int H) {
fl_push_clip(X,Y,W,H);
Fl_Scroll* s = (Fl_Scroll*)v;
@ -92,6 +95,126 @@ void Fl_Scroll::draw_clip(void* v,int X, int Y, int W, int H) {
fl_pop_clip();
}
/**
Calculate visibility/size/position of scrollbars, find children's bounding box.
The \p si paramater will be filled with data from the calculations.
Derived classes can make use of this call to figure out the scrolling area
eg. during resize() handling.
\param[in] si -- ScrollInfo structure
\returns Structure containing the calculated info.
*/
void Fl_Scroll::recalc_scrollbars(ScrollInfo &si) {
// inner box of widget (excluding scrollbars)
si.innerbox_x = x()+Fl::box_dx(box());
si.innerbox_y = y()+Fl::box_dy(box());
si.innerbox_w = w()-Fl::box_dw(box());
si.innerbox_h = h()-Fl::box_dh(box());
// accumulate a bounding box for all the children
si.child_l = si.innerbox_x;
si.child_r = si.innerbox_x;
si.child_b = si.innerbox_y;
si.child_t = si.innerbox_y;
int first = 1;
Fl_Widget*const* a = array();
for (int i=children()-2; i--;) {
Fl_Widget* o = *a++;
if ( first ) {
first = 0;
si.child_l = o->x();
si.child_r = o->x()+o->w();
si.child_b = o->y()+o->h();
si.child_t = o->y();
} else {
if (o->x() < si.child_l) si.child_l = o->x();
if (o->y() < si.child_t) si.child_t = o->y();
if (o->x()+o->w() > si.child_r) si.child_r = o->x()+o->w();
if (o->y()+o->h() > si.child_b) si.child_b = o->y()+o->h();
}
}
// Turn the scrollbars on and off as necessary.
// See if children would fit if we had no scrollbars...
{
int X = si.innerbox_x;
int Y = si.innerbox_y;
int W = si.innerbox_w;
int H = si.innerbox_h;
si.scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
si.vneeded = 0;
si.hneeded = 0;
if (type() & VERTICAL) {
if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
si.vneeded = 1;
W -= si.scrollsize;
if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
}
}
if (type() & HORIZONTAL) {
if ((type() & ALWAYS_ON) || si.child_l < X || si.child_r > X+W) {
si.hneeded = 1;
H -= si.scrollsize;
if (scrollbar.align() & FL_ALIGN_TOP) Y += si.scrollsize;
// recheck vertical since we added a horizontal scrollbar
if (!si.vneeded && (type() & VERTICAL)) {
if ((type() & ALWAYS_ON) || si.child_t < Y || si.child_b > Y+H) {
si.vneeded = 1;
W -= si.scrollsize;
if (scrollbar.align() & FL_ALIGN_LEFT) X += si.scrollsize;
}
}
}
}
si.innerchild_x = X;
si.innerchild_y = Y;
si.innerchild_w = W;
si.innerchild_h = H;
}
// calculate hor scrollbar position
si.hscroll_x = si.innerchild_x;
si.hscroll_y = (scrollbar.align() & FL_ALIGN_TOP)
? si.innerbox_y
: si.innerbox_y + si.innerbox_h - si.scrollsize;
si.hscroll_w = si.innerchild_w;
si.hscroll_h = si.scrollsize;
// calculate ver scrollbar position
si.vscroll_x = (scrollbar.align() & FL_ALIGN_LEFT)
? si.innerbox_x
: si.innerbox_x + si.innerbox_w - si.scrollsize;
si.vscroll_y = si.innerchild_y;
si.vscroll_w = si.scrollsize;
si.vscroll_h = si.innerchild_h;
// calculate h/v scrollbar values (pos/size/first/total)
si.hpos = si.innerchild_x - si.child_l;
si.hsize = si.innerchild_w;
si.hfirst = 0;
si.htotal = si.child_r - si.child_l;
if ( si.hpos < 0 ) { si.htotal += (-si.hpos); si.hfirst = si.hpos; }
si.vpos = si.innerchild_y - si.child_t;
si.vsize = si.innerchild_h;
si.vfirst = 0;
si.vtotal = si.child_b - si.child_t;
if ( si.vpos < 0 ) { si.vtotal += (-si.vpos); si.vfirst = si.vpos; }
// printf("DEBUG --- ScrollInfo ---\n");
// printf("DEBUG scrollsize: %d\n", si.scrollsize);
// printf("DEBUG hneeded, vneeded: %d %d\n", si.hneeded, si.vneeded);
// printf("DEBUG innerbox xywh: %d %d %d %d\n", si.innerbox_x, si.innerbox_y, si.innerbox_w, si.innerbox_h);
// printf("DEBUG innerchild xywh: %d %d %d %d\n", si.innerchild_x, si.innerchild_y, si.innerchild_w, si.innerchild_h);
// printf("DEBUG child lrbt: %d %d %d %d\n", si.child_l, si.child_r, si.child_b, si.child_t);
// printf("DEBUG hscroll xywh: %d %d %d %d\n", si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
// printf("DEBUG vscroll xywh: %d %d %d %d\n", si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
// printf("DEBUG horz scroll vals: %d %d %d %d\n", si.hpos, si.hsize, si.hfirst, si.htotal);
// printf("DEBUG vert scroll vals: %d %d %d %d\n", si.vpos, si.vsize, si.vfirst, si.vtotal);
// printf("DEBUG \n");
}
/**
Returns the bounding box for the interior of the scrolling area, inside
the scrollbars.
@ -157,79 +280,43 @@ void Fl_Scroll::draw() {
}
}
// accumulate bounding box of children:
int l = X; int r = X; int t = Y; int b = Y;
Fl_Widget*const* a = array();
for (int i=children()-2; i--;) {
Fl_Object* o = *a++;
if (o->x() < l) l = o->x();
if (o->y() < t) t = o->y();
if (o->x()+o->w() > r) r = o->x()+o->w();
if (o->y()+o->h() > b) b = o->y()+o->h();
}
// Calculate where scrollbars should go, and draw them
{
ScrollInfo si;
recalc_scrollbars(si);
// turn the scrollbars on and off as necessary:
// See if children would fit if we had no scrollbars...
X = x()+Fl::box_dx(box());
Y = y()+Fl::box_dy(box());
W = w()-Fl::box_dw(box());
H = h()-Fl::box_dh(box());
int vneeded = 0;
int hneeded = 0;
if (type() & VERTICAL) {
if ((type() & ALWAYS_ON) || t < Y || b > Y+H) {
vneeded = 1;
W -= scrollbar.w();
if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar.w();
}
}
if (type() & HORIZONTAL) {
if ((type() & ALWAYS_ON) || l < X || r > X+W) {
hneeded = 1;
H -= hscrollbar.h();
if (scrollbar.align() & FL_ALIGN_TOP) Y += hscrollbar.h();
// recheck vertical since we added a horizontal scrollbar
if (!vneeded && (type() & VERTICAL)) {
if ((type() & ALWAYS_ON) || t < Y || b > Y+H) {
vneeded = 1;
W -= scrollbar.w();
if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar.w();
}
// Now that we know what's needed, make it so.
if (si.vneeded && !scrollbar.visible()) {
scrollbar.set_visible();
d = FL_DAMAGE_ALL;
}
else if (!si.vneeded && scrollbar.visible()) {
scrollbar.clear_visible();
draw_clip(this, si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
d = FL_DAMAGE_ALL;
}
if (si.hneeded && !hscrollbar.visible()) {
hscrollbar.set_visible();
d = FL_DAMAGE_ALL;
}
else if (!si.hneeded && hscrollbar.visible()) {
hscrollbar.clear_visible();
draw_clip(this, si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
d = FL_DAMAGE_ALL;
}
else if ( hscrollbar.h() != si.scrollsize || scrollbar.w() != si.scrollsize ) {
// scrollsize changed
d = FL_DAMAGE_ALL;
}
}
}
// Now that we know what's needed, make it so.
if (vneeded && !scrollbar.visible()) {
scrollbar.set_visible();
d = FL_DAMAGE_ALL;
}
else if (!vneeded && scrollbar.visible()) {
scrollbar.clear_visible();
draw_clip(this,
scrollbar.align()&FL_ALIGN_LEFT ? X : X+W-scrollbar.w(),
Y, scrollbar.w(), H);
d = FL_DAMAGE_ALL;
}
if (hneeded && !hscrollbar.visible()) {
hscrollbar.set_visible();
d = FL_DAMAGE_ALL;
}
else if (!hneeded && hscrollbar.visible()) {
hscrollbar.clear_visible();
draw_clip(this,
X, scrollbar.align()&FL_ALIGN_TOP ? Y : Y+H-hscrollbar.h(),
W, hscrollbar.h());
d = FL_DAMAGE_ALL;
}
scrollbar.resize(scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar.w() : X+W,
Y, scrollbar.w(), H);
scrollbar.value(oldy = yposition_ = (Y-t), H, 0, b-t);
scrollbar.resize(si.vscroll_x, si.vscroll_y, si.vscroll_w, si.vscroll_h);
oldy = yposition_ = si.vpos; // si.innerchild_y - si.child_t;
scrollbar.value(si.vpos, si.vsize, si.vfirst, si.vtotal);
hscrollbar.resize(X,
scrollbar.align()&FL_ALIGN_TOP ? Y-hscrollbar.h() : Y+H,
W, hscrollbar.h());
hscrollbar.value(oldx = xposition_ = (X-l), W, 0, r-l);
hscrollbar.resize(si.hscroll_x, si.hscroll_y, si.hscroll_w, si.hscroll_h);
oldx = xposition_ = si.hpos; // si.innerchild_x - si.child_l;
hscrollbar.value(si.hpos, si.hsize, si.hfirst, si.htotal);
}
// draw the scrollbars:
if (d & FL_DAMAGE_ALL) {
@ -254,7 +341,7 @@ void Fl_Scroll::resize(int X, int Y, int W, int H) {
// move all the children:
Fl_Widget*const* a = array();
for (int i=children()-2; i--;) {
Fl_Object* o = *a++;
Fl_Widget* o = *a++;
o->position(o->x()+dx, o->y()+dy);
}
if (dw==0 && dh==0) {
@ -312,8 +399,9 @@ Fl_Scroll::Fl_Scroll(int X,int Y,int W,int H,const char* L)
hscrollbar(X,Y+H-Fl::scrollbar_size(),
W-Fl::scrollbar_size(),Fl::scrollbar_size()) {
type(BOTH);
xposition_ = 0;
yposition_ = 0;
xposition_ = oldx = 0;
yposition_ = oldy = 0;
scrollbar_size_ = 0;
hscrollbar.type(FL_HORIZONTAL);
hscrollbar.callback(hscrollbar_cb);
scrollbar.callback(scrollbar_cb);

View File

@ -158,6 +158,7 @@ public:
#include "unittest_text.cxx"
#include "unittest_images.cxx"
#include "unittest_viewport.cxx"
#include "unittest_scrollbarsize.cxx"
// callback whenever the browser value changes