fltk/src/fl_draw_arrow.cxx
Albrecht Schlosser fd6accec24 Fix small circle drawing and add doxygen \since statement
src/fl_draw.cxx: improve documentation, add \since 1.4.0,
  simplify scaling code, use forgotten 'color' argument to set the
  circle color.

src/fl_draw_arrow.cxx: add doxygen \since statement
2023-10-15 11:30:19 +02:00

275 lines
7.1 KiB
C++

//
// Arrow drawing code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2023 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>
#include "fl_oxy.h"
// Debug mode: if you design a widget or want to check its layout,
// then enable one or both flags of DEBUG_ARROW (below) so you can
// see where the arrows (i.e. their bounding boxes) are positioned
#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
\since 1.4.0
*/
void fl_draw_arrow(Fl_Rect r, Fl_Arrow_Type t, Fl_Orientation o, Fl_Color col) {
int ret = 0;
Fl_Color saved_color = fl_color();
debug_arrow(r);
// special case: arrows for the "oxy" scheme
if (Fl::is_scheme("oxy")) {
oxy_arrow(r, t, o, col);
return;
}
// implementation of all arrow types for other schemes
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_color(saved_color);
} // fl_draw_arrow()