FLTK logo

[master] e4d8b94 - Add fl_draw_check() to draw better check marks (issue #68)

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] e4d8b94 - Add fl_draw_check() to draw better check marks (issue #68) "Albrecht Schlosser" Nov 15, 2021  
 
commit e4d8b941025fcdd232e748227634529f752381b7
Author:     Albrecht Schlosser <albrechts.fltk@online.de>
AuthorDate: Sun Nov 7 00:20:44 2021 +0100
Commit:     Albrecht Schlosser <albrechts.fltk@online.de>
CommitDate: Mon Nov 15 14:41:31 2021 +0100

    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.

 FL/fl_draw.H             |  13 ++++--
 src/Fl_Check_Browser.cxx |  15 +++----
 src/Fl_Light_Button.cxx  |  43 ++++++++++++-------
 src/fl_draw.cxx          | 108 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+), 28 deletions(-)

diff --git FL/fl_draw.H FL/fl_draw.H
index 9e2e4b3..3d3a544 100644
--- FL/fl_draw.H
+++ 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:
 
 /**
diff --git src/Fl_Check_Browser.cxx src/Fl_Check_Browser.cxx
index 3b32731..a05c4e4 100644
--- src/Fl_Check_Browser.cxx
+++ src/Fl_Check_Browser.cxx
@@ -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());
diff --git src/Fl_Light_Button.cxx src/Fl_Light_Button.cxx
index 4de85bc..71064d9 100644
--- src/Fl_Light_Button.cxx
+++ src/Fl_Light_Button.cxx
@@ -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) {
diff --git src/fl_draw.cxx src/fl_draw.cxx
index ba33db9..efa7a14 100644
--- src/fl_draw.cxx
+++ 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()
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.