Add fl_draw_check() to draw better check marks (issue #68)

This new function can and should be used to draw check marks
in widgets that need it, e.g. Fl_Check_Browser (issue #68) and
Fl_Check_Button.
This commit is contained in:
Albrecht Schlosser 2021-11-07 00:20:44 +01:00
parent d96c980d29
commit e4d8b94102
4 changed files with 151 additions and 28 deletions

View File

@ -1,7 +1,7 @@
//
// Portable drawing function header file for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2020 by Bill Spitzak and others.
// Copyright 1998-2021 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,8 +22,9 @@
#ifndef fl_draw_H
#define fl_draw_H
#include <FL/Enumerations.H> // for the color names
#include <FL/Fl_Graphics_Driver.H> // for fl_graphics_driver + Fl_Region
#include <FL/Enumerations.H> // color names
#include <FL/Fl_Graphics_Driver.H> // fl_graphics_driver + Fl_Region
#include <FL/Fl_Rect.H>
// Image class...
class Fl_Image;
@ -749,10 +750,16 @@ FL_EXPORT void fl_draw(const char* str, int x, int y, int w, int h,
Fl_Image* img=0, int draw_symbols = 1);
// boxtypes:
FL_EXPORT void fl_frame(const char* s, int x, int y, int w, int h);
FL_EXPORT void fl_frame2(const char* s, int x, int y, int w, int h);
FL_EXPORT void fl_draw_box(Fl_Boxtype, int x, int y, int w, int h, Fl_Color);
// basic GUI objects (check marks, arrows, more to come ...):
// Draw a check mark in the given color inside the bounding box bb.
void fl_draw_check(Fl_Rect bb, Fl_Color col);
// images:
/**

View File

@ -184,20 +184,17 @@ void Fl_Check_Browser::item_draw(void *v, int X, int Y, int, int) const {
int cy = Y + (tsize + 1 - CHECK_SIZE) / 2;
X += 2;
// draw the check mark box (always)
fl_color(active_r() ? FL_FOREGROUND_COLOR : fl_inactive(FL_FOREGROUND_COLOR));
fl_loop(X, cy, X, cy + CHECK_SIZE,
X + CHECK_SIZE, cy + CHECK_SIZE, X + CHECK_SIZE, cy);
// draw the check mark
if (i->checked) {
int tx = X + 3;
int tw = CHECK_SIZE - 4;
int d1 = tw / 3;
int d2 = tw - d1;
int ty = cy + (CHECK_SIZE + d2) / 2 - d1 - 2;
for (int n = 0; n < 3; n++, ty++) {
fl_line(tx, ty, tx + d1, ty + d1);
fl_line(tx + d1, ty + d1, tx + tw - 1, ty + d1 - d2 + 1);
}
fl_draw_check(Fl_Rect(X + 1, cy + 1, CHECK_SIZE - 1, CHECK_SIZE - 1), fl_color());
}
// draw the item text
fl_font(textfont(), tsize);
if (i->selected) {
col = fl_contrast(col, selection_color());

View File

@ -32,11 +32,15 @@ void Fl_Light_Button::draw() {
Fl_Color col = value() ? (active_r() ? selection_color() :
fl_inactive(selection_color())) : color();
int W = labelsize();
int W = labelsize(); // check mark box size
if (W > 25) W = 25; // limit box size
int bx = Fl::box_dx(box()); // box frame width
int dx = bx + 2; // relative position of check mark etc.
int dx = bx + 2; // relative position of check mark box
int dy = (h() - W) / 2; // neg. offset o.k. for vertical centering
int lx = 0; // relative label position (STR #3237)
int cx = x() + dx; // check mark box x-position
int cy = y() + dy; // check mark box y-position
int cw = 0; // check mark box width and height
if (down_box()) {
// draw other down_box() styles:
@ -46,22 +50,17 @@ void Fl_Light_Button::draw() {
case _FL_PLASTIC_DOWN_BOX :
case _FL_PLASTIC_UP_BOX :
// Check box...
draw_box(down_box(), x()+dx, y()+dy, W, W, FL_BACKGROUND2_COLOR);
draw_box(down_box(), cx, cy, W, W, FL_BACKGROUND2_COLOR);
if (value()) {
// Check mark...
if (Fl::is_scheme("gtk+")) {
fl_color(FL_SELECTION_COLOR);
} else {
fl_color(col);
}
int tx = x() + dx + 3;
int tw = W - 6;
int d1 = tw/3;
int d2 = tw-d1;
int ty = y() + dy + (W+d2)/2-d1-2;
for (int n = 0; n < 3; n++, ty++) {
fl_line(tx, ty, tx+d1, ty+d1);
fl_line(tx+d1, ty+d1, tx+tw-1, ty+d1-d2+1);
col = FL_SELECTION_COLOR;
}
// Calculate box position and size
cx += Fl::box_dx(down_box());
cy += Fl::box_dy(down_box());
cw = W - Fl::box_dw(down_box());
fl_draw_check(Fl_Rect(cx, cy, cw, cw), col);
}
break;
case _FL_ROUND_DOWN_BOX :
@ -149,7 +148,19 @@ int Fl_Light_Button::handle(int event) {
/**
Creates a new Fl_Light_Button widget using the given
position, size, and label string.
<P>The destructor deletes the check button.
The default box type is \p FL_UP_BOX and the default down box
type down_box() is \p FL_NO_BOX (0).
The selection_color() sets the color of the "light".
Default is FL_YELLOW.
The default label alignment is \p 'FL_ALIGN_LEFT|FL_ALIGN_INSIDE' so
the label is drawn inside the button area right of the "light".
\note Do not change the default box types of Fl_Light_Button. The
box types determine how the button is drawn. If you change the
down_box() type the drawing behavior is undefined.
*/
Fl_Light_Button::Fl_Light_Button(int X, int Y, int W, int H, const char* l)
: Fl_Button(X, Y, W, H, l) {

View File

@ -475,3 +475,111 @@ float fl_override_scale() {
void fl_restore_scale(float s) {
fl_graphics_driver->restore_scale(s);
}
/**
Draw a check mark inside the given bounding box.
The check mark is allowed to fill the entire box but the algorithm used
makes sure that a 1-pixel border is kept free if the box is large enough.
You need to calculate margins for box borders etc. yourself.
The check mark size is limited (minimum and maximum size) and the check
mark is always centered in the given box.
\note If the box is too small (bad GUI design) the check mark will be drawn
over the box borders. This is intentional for better user experience.
Otherwise users might not be able to recognize if a box is checked.
The size limits are implementation details and may be changed at any time.
\param[in] X,Y top left corner of the bounding box
\param[in] W,H width and height of the bounding box
\since 1.4.0
*/
void fl_draw_check(Fl_Rect bb, Fl_Color col) {
const int md = 6; // max. d1 value: 3 * md + 1 pixels wide
int tx = bb.x();
int ty = bb.y();
int tw = bb.w();
int th = bb.h();
int lh = 3; // line height 3 means 4 pixels
int d1, d2;
Fl_Color saved_color = fl_color();
// make sure there's a free 1-pixel border if the area is large enough
if (tw > 10) {
tx++;
tw -= 2;
}
if (th > 10) {
ty++;
th -= 2;
}
// calculate d1, the width and height of the left part of the check mark
d1 = tw / 3;
d2 = 2 * d1;
if (d1 > md) {
d1 = md;
d2 = 2 * d1;
}
// make sure the height fits
if (d2 + lh + 1 > th) {
d2 = th - lh - 1;
d1 = (d2+1) / 2;
}
// check minimal size (box too small)
if (d1 < 2) {
d1 = 2;
d2 = 4;
}
// reduce line height (width) for small sizes
if (d1 < 4)
lh = 2;
tw = d1 + d2 + 1; // total width
th = d2 + lh + 1; // total height
tx = bb.x() + (bb.w() - tw + 1) / 2; // x position (centered)
ty = bb.y() + (bb.h() - th + 1) / 2; // y position (centered)
// Set DEBUG_FRAME to 1 - 3 for debugging (0 otherwise)
// Bit 1 set: draws a green background (the entire given box)
// Bit 2 set: draws a red frame around the check mark (the bounding box)
// The background (1) can be used to test correct positioning by the widget code
#define DEBUG_FRAME (3)
#if (DEBUG_FRAME)
if (DEBUG_FRAME & 1) { // 1 = background
fl_color(0x88dd8800);
fl_rectf(bb.x(), bb.y(), bb.w(), bb.h());
}
if (DEBUG_FRAME & 2) { // 2 = bounding box
fl_color(0xff000000);
fl_rect(tx, ty, tw, th);
}
#endif
// draw the check mark
fl_color(col);
fl_begin_complex_polygon();
ty += d2 - d1; // upper border of check mark: left to right
fl_vertex(tx, ty);
fl_vertex(tx + d1, ty + d1);
fl_vertex(tx + d1 + d2, ty + d1 - d2);
ty += lh; // lower border of check mark: right to left
fl_vertex(tx + d1 + d2, ty + d1 - d2);
fl_vertex(tx + d1, ty + d1);
fl_vertex(tx, ty);
fl_end_complex_polygon();
fl_color(saved_color);
} // fl_draw_check()