Refactor and simplify "arrow drawing" in widgets

"Arrows" in widgets are those GUI elements mostly represented by
triangles pointing in a particular direction as in scrollbars,
choice widgets, some menus, valuators and Fl_Counter widgets.

The code has been simplified and standardized such that all these
GUI elements are drawn identically per FLTK scheme.

Widget authors no longer need to write code to calculate arrow sizes
and draw polygons etc.

Different schemes can and do implement different drawing functions.

Todo: see comments "FIXME_ARROW" in src/Fl_Menu_Button.cxx and
      src/Fl_Menu.cxx
This commit is contained in:
Albrecht Schlosser 2020-11-22 19:19:19 +01:00
parent 4daec2a940
commit ecc47d0cc3
15 changed files with 558 additions and 164 deletions

View File

@ -1,7 +1,7 @@
//
// Enumerations for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2020 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -1223,4 +1223,57 @@ enum Fl_Damage {
// FLTK 1.0.x compatibility definitions (FLTK_1_0_COMPAT) dropped in 1.4.0
/** Arrow types define the type of arrow drawing function.
FLTK schemes can draw several graphical elements in their particular way.
One of these elements is an arrow type that can be in different GUI
elements like scrollbars, choice buttons, and FLTK's Fl_Return_Button.
\note This enum is not yet stable (as of FLTK 1.4.0) and may be changed
without notice as necessary.
\since 1.4.0
*/
enum Fl_Arrow_Type {
FL_ARROW_SINGLE = 0x01, ///< Single arrow, e.g. in Fl_Scrollbar
FL_ARROW_DOUBLE = 0x02, ///< Double arrow, e.g. in Fl_Counter
FL_ARROW_CHOICE = 0x03, ///< Dropdown box, e.g. in Fl_Choice
FL_ARROW_RETURN = 0x04 ///< Return arrow, e.g. in Fl_Return_Button
};
/** Fl_Orientation describes the orientation of a GUI element.
FLTK schemes can draw several graphical elements, for instance arrows,
pointing at different directions. This enum defines the direction
to use for drawing a particular GUI element.
The definition of this enum was chosen such that the enum value can
be multiplied by 45 to get a rotation angle in degrees starting
at the horizontal axis (0 = right, 1 = NE, 2 = up, ...) that can be
used with fl_rotate(). Note: angle is counter-clockwise in degrees.
The 'unspecified' value \b FL_ORIENT_NONE shall be used for elements
that would usually not be rotated, like the return arrow of the
Fl_Return_Button. It can still be used as an angle though since it is
the same value as \p FL_ORIENT_RIGHT (0 degrees).
\note This enum is not yet stable (as of FLTK 1.4.0) and may be changed
without notice as necessary.
\since 1.4.0
*/
enum Fl_Orientation {
FL_ORIENT_NONE = 0x00, ///< GUI element direction is unspecified
FL_ORIENT_RIGHT = 0x00, ///< GUI element pointing right ( 0°)
FL_ORIENT_NE = 0x01, ///< GUI element pointing NE ( 45°)
FL_ORIENT_UP = 0x02, ///< GUI element pointing up ( 90°)
FL_ORIENT_NW = 0x03, ///< GUI element pointing NW (135°)
FL_ORIENT_LEFT = 0x04, ///< GUI element pointing left (180°)
FL_ORIENT_SW = 0x05, ///< GUI element pointing SW (225°)
FL_ORIENT_DOWN = 0x06, ///< GUI element pointing down (270°)
FL_ORIENT_SE = 0x07 ///< GUI element pointing SE (315°)
};
#endif

View File

@ -49,7 +49,7 @@ class FL_EXPORT Fl_Counter : public Fl_Valuator {
Fl_Fontsize textsize_;
Fl_Color textcolor_;
double lstep_;
uchar mouseobj;
uchar mouseobj_;
static void repeat_callback(void *);
int calc_mouseobj();
void increment_cb();
@ -57,6 +57,8 @@ class FL_EXPORT Fl_Counter : public Fl_Valuator {
protected:
void draw();
// compute widths of arrow boxes
void arrow_widths(int &w1, int &w2);
public:

View File

@ -1,7 +1,7 @@
//
// Spinner widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2017 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -66,6 +66,8 @@ protected:
up_button_, // Up button
down_button_; // Down button
virtual void draw();
public:
// Constructor

View File

@ -1,7 +1,7 @@
//
// Portable drawing function header file for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2021 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -250,15 +250,15 @@ inline void fl_line_style(int style, int width = 0, char *dashes = 0) {
fl_graphics_driver->line_style(style, width, dashes);
}
enum {
FL_SOLID = 0, ///< line style: <tt>___________</tt>
FL_DASH = 1, ///< line style: <tt>_ _ _ _ _ _</tt>
FL_DOT = 2, ///< line style: <tt>. . . . . .</tt>
FL_DASHDOT = 3, ///< line style: <tt>_ . _ . _ .</tt>
FL_DASHDOTDOT = 4, ///< line style: <tt>_ . . _ . .</tt>
FL_SOLID = 0, ///< line style: <tt>___________</tt>
FL_DASH = 1, ///< line style: <tt>_ _ _ _ _ _</tt>
FL_DOT = 2, ///< line style: <tt>. . . . . .</tt>
FL_DASHDOT = 3, ///< line style: <tt>_ . _ . _ .</tt>
FL_DASHDOTDOT = 4, ///< line style: <tt>_ . . _ . .</tt>
FL_CAP_FLAT = 0x100, ///< cap style: end is flat
FL_CAP_ROUND = 0x200, ///< cap style: end is round
FL_CAP_SQUARE = 0x300, ///< cap style: end wraps end point
FL_CAP_FLAT = 0x100, ///< cap style: end is flat
FL_CAP_ROUND = 0x200, ///< cap style: end is round
FL_CAP_SQUARE = 0x300, ///< cap style: end wraps end point
FL_JOIN_MITER = 0x1000, ///< join style: line join extends to a point
FL_JOIN_ROUND = 0x2000, ///< join style: line join is rounded
@ -939,6 +939,9 @@ FL_EXPORT void fl_draw_box(Fl_Boxtype, int x, int y, int w, int h, Fl_Color);
// Draw a check mark in the given color inside the bounding box bb.
void fl_draw_check(Fl_Rect bb, Fl_Color col);
// Draw one or more "arrows" (triangles)
FL_EXPORT void fl_draw_arrow(Fl_Rect bb, Fl_Arrow_Type t, Fl_Orientation o, Fl_Color color);
// images:
/**

View File

@ -136,6 +136,7 @@ set (CPPFILES
fl_curve.cxx
fl_diamond_box.cxx
fl_draw.cxx
fl_draw_arrow.cxx
fl_draw_pixmap.cxx
fl_encoding_latin1.cxx
fl_encoding_mac_roman.cxx

View File

@ -1,7 +1,7 @@
//
// Choice widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2015 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -22,6 +22,11 @@
// Emulates the Forms choice widget. This is almost exactly the same
// as an Fl_Menu_Button. The only difference is the appearance of the
// button: it draws the text of the current pick and a down-arrow.
// The exact layout and the type of arrow can vary by FLTK scheme.
// FIXME: all such variations should be implemented in the "scheme code",
// hopefully in a future class derived from a base class Fl_Scheme or similar.
// Albrecht
void Fl_Choice::draw() {
Fl_Boxtype btype = Fl::scheme() ? FL_UP_BOX // non-default uses up box
@ -32,70 +37,74 @@ void Fl_Choice::draw() {
// Arrow area
int H = h() - 2 * dy;
int W = Fl::is_scheme("gtk+") ? 20 : // gtk+ -- fixed size
Fl::is_scheme("gleam") ? 20 : // gleam -- fixed size
Fl::is_scheme("plastic") ? ((H > 20) ? 20 : H) // plastic: shrink if H<20
: ((H > 20) ? 20 : H); // default: shrink if H<20
Fl::is_scheme("gleam") ? 20 // gleam -- fixed size
: ((H > 20) ? 20 : H); // else: shrink if H<20
int X = x() + w() - W - dx;
int Y = y() + dy;
// Arrow object
int w1 = (W - 4) / 3; if (w1 < 1) w1 = 1;
int x1 = X + (W - 2 * w1 - 1) / 2;
int y1 = Y + (H - w1 - 1) / 2;
Fl_Rect ab(X, Y, W, H); // arrow box
int active = active_r();
Fl_Color arrow_color = active ? labelcolor() : fl_inactive(labelcolor());
Fl_Color box_color = color();
// From "original" code: modify the box color *only* for the default scheme.
// This is weird (why?). I believe we should either make sure that the text
// color contrasts well when the text is rendered *or* we should do this for
// *all* schemes. Anyway, adapting the old code... (Albrecht)
if (!Fl::scheme()) { // default scheme only, see comment above
if (fl_contrast(textcolor(), FL_BACKGROUND2_COLOR) == textcolor())
box_color = FL_BACKGROUND2_COLOR;
else
box_color = fl_lighter(color());
}
// Draw the widget box
draw_box(btype, box_color);
// Arrow box or horizontal divider line, depending on the current scheme
// Scheme: Box or divider line
// ----------------------------------------
// Default (None): Arrow box (FL_UP_BOX)
// gtk+, gleam: Divider line
// else: Nothing (!)
if (Fl::scheme()) {
// NON-DEFAULT SCHEME
// Draw widget box
draw_box(btype, color());
// Draw arrow area
fl_color(active_r() ? labelcolor() : fl_inactive(labelcolor()));
if (Fl::is_scheme("plastic")) {
// Show larger up/down arrows...
fl_polygon(x1, y1 + 3, x1 + w1, y1 + w1 + 3, x1 + 2 * w1, y1 + 3);
fl_polygon(x1, y1 + 1, x1 + w1, y1 - w1 + 1, x1 + 2 * w1, y1 + 1);
} else {
// Show smaller up/down arrows with a divider...
x1 = x() + w() - 13 - dx;
y1 = y() + h() / 2;
fl_polygon(x1, y1 - 2, x1 + 3, y1 - 5, x1 + 6, y1 - 2);
fl_polygon(x1, y1 + 2, x1 + 3, y1 + 5, x1 + 6, y1 + 2);
if (Fl::is_scheme("gtk+") ||
Fl::is_scheme("gleam")) {
// draw the divider
int x1 = x() + w() - 20 - dx;
int y1 = y() + h() / 2;
fl_color(fl_darker(color()));
fl_yxline(x1 - 7, y1 - 8, y1 + 8);
fl_yxline(x1, y1 - 8, y1 + 8);
fl_color(fl_lighter(color()));
fl_yxline(x1 - 6, y1 - 8, y1 + 8);
fl_yxline(x1 + 1, y1 - 8, y1 + 8);
}
} else {
// DEFAULT SCHEME
// Draw widget box
if (fl_contrast(textcolor(), FL_BACKGROUND2_COLOR) == textcolor()) {
draw_box(btype, FL_BACKGROUND2_COLOR);
} else {
draw_box(btype, fl_lighter(color()));
}
// Draw arrow area
draw_box(FL_UP_BOX,X,Y,W,H,color());
fl_color(active_r() ? labelcolor() : fl_inactive(labelcolor()));
fl_polygon(x1, y1, x1 + w1, y1 + w1, x1 + 2 * w1, y1);
} else { // Default scheme ("None")
// Draw arrow box
draw_box(FL_UP_BOX, X, Y, W, H, color());
ab.inset(FL_UP_BOX);
}
// Draw choice arrow(s)
fl_draw_arrow(ab, FL_ARROW_CHOICE, FL_ORIENT_NONE, arrow_color);
W += 2 * dx;
// Draw menu item's label
if (mvalue()) {
Fl_Menu_Item m = *mvalue();
if (active_r()) m.activate(); else m.deactivate();
if (active) m.activate(); else m.deactivate();
// Clip
int xx = x() + dx, yy = y() + dy + 1, ww = w() - W, hh = H - 2;
fl_push_clip(xx, yy, ww, hh);
if ( Fl::scheme()) {
if (Fl::scheme()) {
Fl_Label l;
l.value = m.text;
l.image = 0;

View File

@ -19,67 +19,118 @@
#include <FL/Fl_Simple_Counter.H>
#include <FL/fl_draw.H>
void Fl_Counter::draw() {
int i; Fl_Boxtype boxtype[5];
Fl_Color selcolor;
// This struct describes the four arrow boxes
struct arrow_box {
int width;
Fl_Arrow_Type arrow_type;
Fl_Boxtype boxtype;
Fl_Orientation orientation;
arrow_box() { // constructor
width = 0;
boxtype = FL_NO_BOX;
orientation = FL_ORIENT_RIGHT;
arrow_type = FL_ARROW_SINGLE;
}
};
boxtype[0] = box();
if (boxtype[0] == FL_UP_BOX) boxtype[0] = FL_DOWN_BOX;
if (boxtype[0] == FL_THIN_UP_BOX) boxtype[0] = FL_THIN_DOWN_BOX;
for (i=1; i<5; i++)
if (mouseobj == i)
boxtype[i] = fl_down(box());
else
boxtype[i] = box();
/**
Compute sizes (widths) of arrow boxes.
int xx[5], ww[5];
if (type() == FL_NORMAL_COUNTER) {
int W = w()*15/100;
xx[1] = x(); ww[1] = W;
xx[2] = x()+1*W; ww[2] = W;
xx[0] = x()+2*W; ww[0] = w()-4*W;
xx[3] = x()+w()-2*W; ww[3] = W;
xx[4] = x()+w()-1*W; ww[4] = W;
This method computes the two sizes of the arrow boxes of Fl_Counter.
You can override it in a subclass if you want to draw fancy arrows
or change the layout. However, the basic layout is fixed and can't
be changed w/o overriding the draw() and handle() methods.
Basic layout:
\code
+------+-----+-------------+-----+------+
| << | < | value | > | >> |
+------+-----+-------------+-----+------+
\endcode
The returned value \p w2 should be zero if the counter type() is FL_SIMPLE_COUNTER.
\param[out] w1 width of single arrow box
\param[out] w2 width of double arrow box
*/
void Fl_Counter::arrow_widths(int &w1, int &w2) {
if (type() == FL_SIMPLE_COUNTER) {
w1 = w() * 20/100;
w2 = 0;
} else {
int W = w()*20/100;
xx[1] = 0; ww[1] = 0;
xx[2] = x(); ww[2] = W;
xx[0] = x()+W; ww[0] = w()-2*W;
xx[3] = x()+w()-1*W; ww[3] = W;
xx[4] = 0; ww[4] = 0;
w1 = w() * 13/100;
w2 = w() * 17/100;
}
// limit arrow box sizes to reserve more space for the text box
if (w1 > 18) w1 = 18;
if (w2 > 24) w2 = 24;
}
void Fl_Counter::draw() {
struct arrow_box ab[4];
// text box setup
Fl_Boxtype tbt = box();
if (tbt == FL_UP_BOX) tbt = FL_DOWN_BOX;
if (tbt == FL_THIN_UP_BOX) tbt = FL_THIN_DOWN_BOX;
// array boxes
for (int i = 0; i < 4; i++) {
if (mouseobj_ == i + 1)
ab[i].boxtype = fl_down(box());
else
ab[i].boxtype = box();
}
draw_box(boxtype[0], xx[0], y(), ww[0], h(), FL_BACKGROUND2_COLOR);
ab[0].arrow_type = ab[3].arrow_type = FL_ARROW_DOUBLE; // first and last arrow
ab[0].orientation = ab[1].orientation = FL_ORIENT_LEFT; // left arrows
int w1 = 0, w2 = 0;
arrow_widths(w1, w2);
if (type() == FL_SIMPLE_COUNTER)
w2 = 0;
ab[0].width = ab[3].width = w2; // double arrows
ab[1].width = ab[2].width = w1; // single arrows
int tw = w() - 2 * (w1 + w2); // text box width
int tx = x() + w1 + w2; // text box position
// printf("w() = %3d, w1 = %3d, w2 = %3d, tw = %3d\n", w(), w1, w2, tw);
// always draw text box and text
draw_box(tbt, tx, y(), tw, h(), FL_BACKGROUND2_COLOR);
fl_font(textfont(), textsize());
fl_color(active_r() ? textcolor() : fl_inactive(textcolor()));
char str[128]; format(str);
fl_draw(str, xx[0], y(), ww[0], h(), FL_ALIGN_CENTER);
if (Fl::focus() == this) draw_focus(boxtype[0], xx[0], y(), ww[0], h());
fl_draw(str, tx, y(), tw, h(), FL_ALIGN_CENTER);
if (Fl::focus() == this) draw_focus(tbt, tx, y(), tw, h());
if (!(damage()&FL_DAMAGE_ALL)) return; // only need to redraw text
Fl_Color arrow_color;
if (active_r())
selcolor = labelcolor();
arrow_color = labelcolor();
else
selcolor = fl_inactive(labelcolor());
arrow_color = fl_inactive(labelcolor());
if (type() == FL_NORMAL_COUNTER) {
draw_box(boxtype[1], xx[1], y(), ww[1], h(), color());
fl_draw_symbol("@-4<<", xx[1], y(), ww[1], h(), selcolor);
// draw arrow boxes
int xo = x();
for (int i = 0; i < 4; i++) {
if (ab[i].width > 0) {
draw_box(ab[i].boxtype, xo, y(), ab[i].width, h(), color());
Fl_Rect bb(xo, y(), ab[i].width, h(), ab[i].boxtype);
fl_draw_arrow(bb, ab[i].arrow_type, ab[i].orientation, arrow_color);
xo += ab[i].width;
}
if (i == 1) xo += tw;
}
draw_box(boxtype[2], xx[2], y(), ww[2], h(), color());
fl_draw_symbol("@-4<", xx[2], y(), ww[2], h(), selcolor);
draw_box(boxtype[3], xx[3], y(), ww[3], h(), color());
fl_draw_symbol("@-4>", xx[3], y(), ww[3], h(), selcolor);
if (type() == FL_NORMAL_COUNTER) {
draw_box(boxtype[4], xx[4], y(), ww[4], h(), color());
fl_draw_symbol("@-4>>", xx[4], y(), ww[4], h(), selcolor);
}
}
} // draw()
void Fl_Counter::increment_cb() {
if (!mouseobj) return;
if (!mouseobj_) return;
double v = value();
switch (mouseobj) {
switch (mouseobj_) {
case 1: v -= lstep_; break;
case 2: v = increment(v, -1); break;
case 3: v = increment(v, 1); break;
@ -95,7 +146,7 @@ void Fl_Counter::repeat_callback(void* v) {
Fl_Counter* b = (Fl_Counter*)v;
int buttons = Fl::event_state() & FL_BUTTONS; // any mouse button pressed
int focus = (Fl::focus() == b); // the widget has focus
if (b->mouseobj && buttons && focus) {
if (b->mouseobj_ && buttons && focus) {
Fl::add_timeout(REPEAT, repeat_callback, b);
b->increment_cb();
}
@ -120,9 +171,9 @@ int Fl_Counter::handle(int event) {
int i;
switch (event) {
case FL_RELEASE:
if (mouseobj) {
if (mouseobj_) {
Fl::remove_timeout(repeat_callback, this);
mouseobj = 0;
mouseobj_ = 0;
redraw();
}
handle_release();
@ -135,9 +186,9 @@ int Fl_Counter::handle(int event) {
}
case FL_DRAG:
i = calc_mouseobj();
if (i != mouseobj) {
if (i != mouseobj_) {
Fl::remove_timeout(repeat_callback, this);
mouseobj = (uchar)i;
mouseobj_ = (uchar)i;
if (i > 0)
Fl::add_timeout(INITIALREPEAT, repeat_callback, this);
Fl_Widget_Tracker wp(this);
@ -159,7 +210,7 @@ int Fl_Counter::handle(int event) {
}
// break not required because of switch...
case FL_UNFOCUS :
mouseobj = 0;
mouseobj_ = 0;
/* FALLTHROUGH */
case FL_FOCUS :
if (Fl::visible_focus()) {
@ -195,7 +246,7 @@ Fl_Counter::Fl_Counter(int X, int Y, int W, int H, const char* L)
bounds(-1000000.0, 1000000.0);
Fl_Valuator::step(1, 10);
lstep_ = 1.0;
mouseobj = 0;
mouseobj_ = 0;
textfont_ = FL_HELVETICA;
textsize_ = FL_NORMAL_SIZE;
textcolor_ = FL_FOREGROUND_COLOR;

View File

@ -1,7 +1,7 @@
//
// Menu code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2015 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -496,6 +496,7 @@ void menuwindow::drawentry(const Fl_Menu_Item* m, int n, int eraseit) {
int sz = (hh-7)&-2;
int y1 = yy+(hh-sz)/2;
int x1 = xx+ww-sz-3;
// FIXME_ARROW: use fl_draw_arrow()
fl_polygon(x1+2, y1, x1+2, y1+sz, x1+sz/2+2, y1+sz/2);
} else if (m->shortcut_) {
Fl_Font f = m->labelsize_ || m->labelfont_ ? (Fl_Font)m->labelfont_ :

View File

@ -1,7 +1,7 @@
//
// Menu button widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2019 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -31,6 +31,7 @@ void Fl_Menu_Button::draw() {
draw_label(x()+Fl::box_dx(box()), y(), X-x()+2, h());
if (Fl::focus() == this) draw_focus();
// ** if (box() == FL_FLAT_BOX) return; // for XForms compatibility
// FIXME_ARROW: use fl_draw_arrow()
fl_color(active_r() ? FL_DARK3 : fl_inactive(FL_DARK3));
fl_line(X+H/2, Y+H, X, Y, X+H, Y);
fl_color(active_r() ? FL_LIGHT3 : fl_inactive(FL_LIGHT3));

View File

@ -1,7 +1,7 @@
//
// Scroll bar widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2015 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -196,61 +196,56 @@ int Fl_Scrollbar::handle(int event) {
}
void Fl_Scrollbar::draw() {
if (damage()&FL_DAMAGE_ALL) draw_box();
int X = x()+Fl::box_dx(box());
int Y = y()+Fl::box_dy(box());
int W = w()-Fl::box_dw(box());
int H = h()-Fl::box_dh(box());
if (damage() & FL_DAMAGE_ALL) draw_box();
int X = x() + Fl::box_dx(box());
int Y = y() + Fl::box_dy(box());
int W = w() - Fl::box_dw(box());
int H = h() - Fl::box_dh(box());
Fl_Rect ab; // arrow box
int inset = 3;
if (W < 8 || H < 8)
inset = 2;
if (horizontal()) {
if (W < 3*H) {Fl_Slider::draw(X,Y,W,H); return;}
Fl_Slider::draw(X+H,Y,W-2*H,H);
if (W < 3*H) {
Fl_Slider::draw(X, Y, W, H);
return;
}
Fl_Slider::draw(X+H, Y, W-2*H, H);
if (damage()&FL_DAMAGE_ALL) {
draw_box((pushed_==1) ? fl_down(slider()) : slider(),
X, Y, H, H, selection_color());
draw_box((pushed_==2) ? fl_down(slider()) : slider(),
X+W-H, Y, H, H, selection_color());
if (active_r())
fl_color(labelcolor());
else
fl_color(fl_inactive(labelcolor()));
int w1 = (H-4)/3; if (w1 < 1) w1 = 1;
int x1 = X+(H-w1-1)/2;
int yy1 = Y+(H-2*w1-1)/2;
if (Fl::is_scheme("gtk+")) {
fl_polygon(x1, yy1+w1, x1+w1, yy1+2*w1, x1+w1-1, yy1+w1, x1+w1, yy1);
x1 += (W-H);
fl_polygon(x1, yy1, x1+1, yy1+w1, x1, yy1+2*w1, x1+w1, yy1+w1);
} else {
fl_polygon(x1, yy1+w1, x1+w1, yy1+2*w1, x1+w1, yy1);
x1 += (W-H);
fl_polygon(x1, yy1, x1, yy1+2*w1, x1+w1, yy1+w1);
}
Fl_Color arrowcolor = active_r() ? labelcolor() : fl_inactive(labelcolor());
ab = Fl_Rect(X, Y, H, H);
ab.inset(inset);
fl_draw_arrow(ab, FL_ARROW_SINGLE, FL_ORIENT_LEFT, arrowcolor); // left arrow
ab = Fl_Rect(X+W-H, Y, H, H);
ab.inset(inset);
fl_draw_arrow(ab, FL_ARROW_SINGLE, FL_ORIENT_RIGHT, arrowcolor); // right arrow
}
} else { // vertical
if (H < 3*W) {Fl_Slider::draw(X,Y,W,H); return;}
Fl_Slider::draw(X,Y+W,W,H-2*W);
if (damage()&FL_DAMAGE_ALL) {
if (H < 3*W) {
Fl_Slider::draw(X, Y, W, H);
return;
}
Fl_Slider::draw(X, Y+W, W, H-2*W);
if (damage() & FL_DAMAGE_ALL) {
draw_box((pushed_==1) ? fl_down(slider()) : slider(),
X, Y, W, W, selection_color());
draw_box((pushed_==2) ? fl_down(slider()) : slider(),
X, Y+H-W, W, W, selection_color());
if (active_r())
fl_color(labelcolor());
else
fl_color(fl_inactive(labelcolor()));
int w1 = (W-4)/3; if (w1 < 1) w1 = 1;
int x1 = X+(W-2*w1-1)/2;
int yy1 = Y+(W-w1-1)/2;
if (Fl::is_scheme("gtk+")) {
fl_polygon(x1, yy1+w1, x1+w1, yy1+w1-1, x1+2*w1, yy1+w1, x1+w1, yy1);
yy1 += H-W;
fl_polygon(x1, yy1, x1+w1, yy1+1, x1+w1, yy1+w1);
fl_polygon(x1+w1, yy1+1, x1+2*w1, yy1, x1+w1, yy1+w1);
} else {
fl_polygon(x1, yy1+w1, x1+2*w1, yy1+w1, x1+w1, yy1);
yy1 += H-W;
fl_polygon(x1, yy1, x1+w1, yy1+w1, x1+2*w1, yy1);
}
Fl_Color arrowcolor = active_r() ? labelcolor() : fl_inactive(labelcolor());
ab = Fl_Rect(X, Y, W, W);
ab.inset(inset);
fl_draw_arrow(ab, FL_ARROW_SINGLE, FL_ORIENT_UP, arrowcolor); // up arrow
ab = Fl_Rect(X, Y+H-W, W, W);
ab.inset(inset);
fl_draw_arrow(ab, FL_ARROW_SINGLE, FL_ORIENT_DOWN, arrowcolor); // down arrow
}
}
}

View File

@ -1,7 +1,7 @@
//
// Spinner widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2017 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -21,6 +21,8 @@
#include <stdlib.h>
#include <FL/Fl_Spinner.H>
#include <FL/Fl_Rect.H>
#include <FL/fl_draw.H>
/*
This widget is a combination of the input widget and repeat buttons.
@ -91,9 +93,6 @@ void Fl_Spinner::update() {
input_.value(s);
}
#define FL_UP_ARROW_TX "@-42<"
#define FL_DOWN_ARROW_TX "@-42>"
/**
Creates a new Fl_Spinner widget using the given position, size,
and label string.
@ -104,9 +103,8 @@ void Fl_Spinner::update() {
Fl_Spinner::Fl_Spinner(int X, int Y, int W, int H, const char *L)
: Fl_Group(X, Y, W, H, L),
input_(X, Y, W - H / 2 - 2, H),
up_button_(X + W - H / 2 - 2, Y, H / 2 + 2, H / 2, FL_UP_ARROW_TX),
down_button_(X + W - H / 2 - 2, Y + H - H / 2,
H / 2 + 2, H / 2, FL_DOWN_ARROW_TX)
up_button_(X + W - H / 2 - 2, Y, H / 2 + 2, H / 2),
down_button_(X + W - H / 2 - 2, Y + H - H / 2, H / 2 + 2, H / 2)
{
end();
@ -129,6 +127,26 @@ Fl_Spinner::Fl_Spinner(int X, int Y, int W, int H, const char *L)
down_button_.callback((Fl_Callback *)sb_cb, this);
}
void Fl_Spinner::draw() {
// draw the box and the input widget
draw_box();
((Fl_Widget&)input_).draw();
// draw the buttons and the up and down arrows as their "labels"
((Fl_Widget&)up_button_).draw();
Fl_Rect up(up_button_);
up.inset(up_button_.box());
fl_draw_arrow(up, FL_ARROW_SINGLE, FL_ORIENT_UP, labelcolor());
((Fl_Widget&)down_button_).draw();
Fl_Rect down(down_button_);
down.inset(down_button_.box());
fl_draw_arrow(down, FL_ARROW_SINGLE, FL_ORIENT_DOWN, labelcolor());
}
int Fl_Spinner::handle(int event) {
switch (event) {

View File

@ -139,6 +139,7 @@ CPPFILES = \
fl_curve.cxx \
fl_diamond_box.cxx \
fl_draw.cxx \
fl_draw_arrow.cxx \
fl_draw_pixmap.cxx \
fl_encoding_latin1.cxx \
fl_encoding_mac_roman.cxx \

View File

@ -1,7 +1,7 @@
//
// Box drawing code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2020 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -415,7 +415,7 @@ Fl_Box_Draw_F *Fl::get_boxtype(Fl_Boxtype t) {
}
/** Sets the function to call to draw a specific boxtype. */
void Fl::set_boxtype(Fl_Boxtype t, Fl_Box_Draw_F* f,
uchar a, uchar b, uchar c, uchar d) {
uchar a, uchar b, uchar c, uchar d) {
fl_box_table[t].f = f;
fl_box_table[t].set = 1;
fl_box_table[t].dx = a;

View File

@ -1,7 +1,7 @@
//
// Label drawing code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2020 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -249,8 +249,9 @@ void fl_draw(
if (str) {
int desc = fl_descent();
for (p=str; ; ypos += height) {
if (lines>1) e = expand_text_(p, linebuf, 0, w - symtotal - imgtotal, buflen,
width, align&FL_ALIGN_WRAP, draw_symbols);
if (lines>1)
e = expand_text_(p, linebuf, 0, w - symtotal - imgtotal, buflen,
width, align&FL_ALIGN_WRAP, draw_symbols);
else e = "";
if (width > symoffset) symoffset = (int)(width + 0.5);

256
src/fl_draw_arrow.cxx Normal file
View File

@ -0,0 +1,256 @@
//
// Arrow drawing code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
// These functions implement drawing of all "arrow like" GUI elements in scrollbars,
// choice widgets, menus, etc.
// Implementation of fl_draw_arrow(...) dependent on the active FLTK Scheme.
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/fl_utf8.h>
#ifndef DEBUG_ARROW
#define DEBUG_ARROW (0) // 0 = off, 1 = green background, 2 = red frame, 3 = both
#endif
void debug_arrow(Fl_Rect r) {
#if (DEBUG_ARROW & 1)
fl_color(fl_lighter(FL_GREEN));
fl_rectf(r);
#endif
#if (DEBUG_ARROW & 2)
fl_color(FL_RED);
fl_line_style(FL_SOLID, 1); // work around X11 bug with default line width 0
fl_rect(r);
fl_line_style(FL_SOLID, 0); // reset line style
#endif
} // debug_arrow
// Calculate the applicable arrow size.
// Imagine an arrow pointing to the right side:
// - the calculated size s is the width of the arrow,
// - the height of the arrow is 2 * s.
// The calculation takes into account that we need one pixel padding at
// all sides and that the available space doesn't need to be a square,
// i.e. it's possible that r.w() != r.h().
static int arrow_size(Fl_Rect r, Fl_Orientation o, int num = 1) {
int s, d1, d2;
switch(o) {
case FL_ORIENT_LEFT:
case FL_ORIENT_RIGHT:
d1 = (r.w() - 2) / num;
d2 = (r.h() - 2) / 2;
break;
default: // up or down arrow
d1 = (r.h() - 2) / num;
d2 = (r.w() - 2) / 2;
break;
}
s = d1 < d2 ? d1 : d2;
if (s < 2) s = 2;
else if (s > 6) s = 6;
return s;
}
// Draw a "Single Arrow" in an arbitrary direction (0°, 90°, 180°, 270°).
// This is the basic arrow drawing function for all "standard" widgets.
// It is used in Fl_Scrollbars and similar and in all combinations, for
// instance when "Double Arrows" or other combinations are needed.
static int fl_draw_arrow_single(Fl_Rect r, Fl_Orientation o, Fl_Color col, int d = -1) {
int x1, y1;
x1 = r.x();
y1 = r.y();
if (d < 0)
d = arrow_size(r, o);
fl_color(col);
switch(o) {
case FL_ORIENT_LEFT:
x1 += (r.w()-d)/2 - 1;
y1 += r.h()/2;
if (Fl::is_scheme("gtk+"))
fl_polygon(x1, y1, x1+d, y1-d, x1+d-1, y1, x1+d, y1+d);
else
fl_polygon(x1, y1, x1+d, y1-d, x1+d, y1+d);
return 1;
case FL_ORIENT_RIGHT:
x1 += (r.w()-d)/2;
y1 += r.h()/2;
if (Fl::is_scheme("gtk+"))
fl_polygon(x1, y1-d, x1+1, y1, x1, y1+d, x1+d, y1);
else
fl_polygon(x1, y1-d, x1, y1+d, x1+d, y1);
return 1;
case FL_ORIENT_UP:
x1 += r.w()/2;
y1 += (r.h()-d)/2 - 1;
if (Fl::is_scheme("gtk+"))
fl_polygon(x1, y1, x1+d, y1+d, x1, y1+d-1, x1-d, y1+d);
else
fl_polygon(x1, y1, x1+d, y1+d, x1-d, y1+d);
return 1;
case FL_ORIENT_DOWN:
x1 += r.w()/2-d;
y1 += (r.h()-d)/2;
if (Fl::is_scheme("gtk+")) {
fl_polygon(x1, y1, x1+d, y1+1, x1+d, y1+d);
fl_polygon(x1+d, y1+1, x1+2*d, y1, x1+d, y1+d);
} else {
fl_polygon(x1, y1, x1+d, y1+d, x1+2*d, y1);
}
return 1;
default: // orientation not handled: return error
return 0;
}
return 0;
} // fl_draw_arrow_single()
// Draw a "Double Arrow" in an arbitrary direction (0°, 90°, 180°, 270°).
// This is the basic arrow drawing function for all "standard" widgets.
// It is used in Fl_Scrollbars and similar and in all combinations, for
// instance when "Double Arrows" or other combinations are needed.
static int fl_draw_arrow_double(Fl_Rect r, Fl_Orientation o, Fl_Color col) {
int d = arrow_size(r, o, 2);
int x1 = r.x();
int y1 = r.y();
int da = (d+1)/2;
switch(o) {
case FL_ORIENT_LEFT:
case FL_ORIENT_RIGHT:
r.x(x1 - da);
fl_draw_arrow_single(r, o, col, d);
r.x(x1 + da);
return fl_draw_arrow_single(r, o, col, d);
case FL_ORIENT_UP:
case FL_ORIENT_DOWN:
r.y(y1 - da);
fl_draw_arrow_single(r, o, col, d);
r.y(y1 + da);
return fl_draw_arrow_single(r, o, col, d);
default: // orientation not handled: return error
return 0;
}
return 0;
} // fl_draw_arrow_double()
// Draw a "Choice Arrow". The direction and type is determined by the scheme.
static int fl_draw_arrow_choice(Fl_Rect r, Fl_Color col) {
int w1 = (r.w() - 4) / 3; if (w1 < 1) w1 = 1;
int x1 = r.x() + (r.w() - 2 * w1 - 1) / 2;
int y1 = r.y() + (r.h() - w1 - 1) / 2;
if (Fl::is_scheme("gtk+") ||
Fl::is_scheme("gleam")) {
// Show smaller up/down arrows ...
int x1 = r.x() + (r.w() - 6)/2;
int y1 = r.y() + r.h() / 2;
fl_color(col);
fl_polygon(x1, y1 - 2, x1 + 3, y1 - 5, x1 + 6, y1 - 2);
fl_polygon(x1, y1 + 2, x1 + 3, y1 + 5, x1 + 6, y1 + 2);
return 1;
}
else if (Fl::is_scheme("plastic")) {
// Show larger up/down arrows...
fl_color(col);
fl_polygon(x1, y1 + 3, x1 + w1, y1 + w1 + 3, x1 + 2 * w1, y1 + 3);
fl_polygon(x1, y1 + 1, x1 + w1, y1 - w1 + 1, x1 + 2 * w1, y1 + 1);
return 1;
}
else { // none, default // single down arrow
return fl_draw_arrow_single(r, FL_ORIENT_DOWN, col);
}
return 0;
} // fl_draw_arrow_double()
/**
Draw an "arrow like" GUI element for the selected scheme.
In the future this function should be integrated in Fl_Scheme
as a virtual method, i.e. it would call a method like ...
\code
Fl_Scheme::current()->draw_arrow(r, t, o, col);
\endcode
\param[in] r bounding box
\param[in] t arrow type
\param[in] o orientation
\param[in] col arrow color
*/
void fl_draw_arrow(Fl_Rect r, Fl_Arrow_Type t, Fl_Orientation o, Fl_Color col) {
int ret = 0;
debug_arrow(r);
// implementation of all arrow types
switch(t) {
case FL_ARROW_SINGLE:
ret = fl_draw_arrow_single(r, o, col);
break;
case FL_ARROW_DOUBLE:
ret = fl_draw_arrow_double(r, o, col);
break;
case FL_ARROW_CHOICE:
ret = fl_draw_arrow_choice(r, col);
break;
default: // unknown arrow type
ret = 0;
break;
}
// draw an error flag (red rectangle with cross) if not successful
if (!ret) {
fl_color(FL_RED);
fl_rectf(r);
fl_color(FL_BLACK);
fl_rect(r);
fl_line(r.x(), r.y(), r.r(), r.b());
fl_line(r.x(), r.b(), r.r(), r.y());
}
} // fl_draw_arrow()