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:
parent
d96c980d29
commit
e4d8b94102
13
FL/fl_draw.H
13
FL/fl_draw.H
@ -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:
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
108
src/fl_draw.cxx
108
src/fl_draw.cxx
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user