Improved documentation for fl_draw()

There are no code changes in this commit, only added
documentation and improved formatting.
This commit is contained in:
Matthias Melcher 2024-08-13 20:57:59 +02:00
parent 85d1d904ec
commit 9f04497d6e

View File

@ -38,15 +38,36 @@ char fl_draw_shortcut; // set by fl_labeltypes.cxx
static char* underline_at; static char* underline_at;
/* If called with maxbuf==0, use an internally allocated buffer and enlarge it as needed. /*
Extract the part of text that fits into the given maximum width.
If called with maxbuf==0, use an internally allocated buffer and enlarge it as needed.
Otherwise, use buf as buffer but don't go beyond its length of maxbuf. Otherwise, use buf as buffer but don't go beyond its length of maxbuf.
\param[in] from input text, can contain '\n' and single or double '\@' characters
\param[out] buf buffer for the output text segment
\param[in] maxbuf size of the buffer, or 0 to use the internal buffer allocated in this function
\param[in] maxw maximum width in pixels of the output text
\param[out] n number of characters in the output text segment
\param[out] width actual width in pixels of the output text
\param[in] wrap if true, wrap at maxw, else wrap at newlines
\param[in] draw_symbols if true, double '\@' characters are escaped into a single '@'
\return pointer to the next character in the input text
*/ */
static const char* expand_text_(const char* from, char*& buf, int maxbuf, double maxw, int& n, static const char* expand_text_(const char* from, char*& buf, int maxbuf, double maxw, int& n,
double &width, int wrap, int draw_symbols) { double &width, int wrap, int draw_symbols) {
underline_at = 0; // Reset underline_at to null
underline_at = NULL;
// Initialize the total width to 0
double w = 0; double w = 0;
// Check if the caller wants to use the internal buffer
static int l_local_buff = 500; static int l_local_buff = 500;
static char *local_buf = (char*)malloc(l_local_buff); // initial buffer allocation static char *local_buf = (char*)malloc(l_local_buff); // initial buffer allocation
// Calculate the end pointer of the buffer
char* e; char* e;
if (maxbuf == 0) { if (maxbuf == 0) {
buf = local_buf; buf = local_buf;
@ -54,32 +75,48 @@ static const char* expand_text_(const char* from, char*& buf, int maxbuf, double
} else { } else {
e = buf+(maxbuf-4); e = buf+(maxbuf-4);
} }
// Initialize the output pointer to the buffer
char* o = buf; char* o = buf;
// Initialize the word end pointer that points into the `out` buffer
char* word_end = o; char* word_end = o;
// Initialize the word start pointer that points into the `from` buffer
const char* word_start = from; const char* word_start = from;
// Iterate over the input text
const char* p = from; const char* p = from;
for (;; p++) { for (;; p++) {
int c = *p & 255; int c = *p & 255;
// Check for end of line, space, or '\n'
if (!c || c == ' ' || c == '\n') { if (!c || c == ' ' || c == '\n') {
// test for word-wrap: // Check for word wrap
if (word_start < p && wrap) { if (word_start < p && wrap) {
// Calculate the new width
double newwidth = w + fl_width(word_end, (int) (o-word_end) ); double newwidth = w + fl_width(word_end, (int) (o-word_end) );
// Check if the new width exceeds the maximum width
if (word_end > buf && int(newwidth) > maxw) { // break before this word if (word_end > buf && int(newwidth) > maxw) { // break before this word
o = word_end; o = word_end;
p = word_start; p = word_start;
break; break;
} }
// Update the word end pointer
word_end = o; word_end = o;
w = newwidth; w = newwidth;
} }
// Check for end of line
if (!c) break; if (!c) break;
else if (c == '\n') {p++; break;} else if (c == '\n') {p++; break;}
// Update the word start pointer
word_start = p+1; word_start = p+1;
} }
// Check if the buffer needs to be enlarged
if (o > e) { if (o > e) {
if (maxbuf) break; // don't overflow buffer if (maxbuf) break; // don't overflow buffer
l_local_buff += int(o - e) + 200; // enlarge buffer l_local_buff += int(o - e) + 200; // enlarge buffer
@ -92,27 +129,37 @@ static const char* expand_text_(const char* from, char*& buf, int maxbuf, double
word_end = local_buf + delta_end; word_end = local_buf + delta_end;
} }
// Process the character based on its type
if (c == '\t') { if (c == '\t') {
// Process tab character
for (c = fl_utf_nb_char((uchar*)buf, (int) (o-buf) )%8; c<8 && o<e; c++) for (c = fl_utf_nb_char((uchar*)buf, (int) (o-buf) )%8; c<8 && o<e; c++)
*o++ = ' '; *o++ = ' ';
} else if (c == '&' && fl_draw_shortcut && *(p+1)) { } else if (c == '&' && fl_draw_shortcut && *(p+1)) {
// Process '&' character
if (*(p+1) == '&') {p++; *o++ = '&';} if (*(p+1) == '&') {p++; *o++ = '&';}
else if (fl_draw_shortcut != 2) underline_at = o; else if (fl_draw_shortcut != 2) underline_at = o;
} else if (c < ' ' || c == 127) { // ^X } else if (c < ' ' || c == 127) { // ^X
// Process control characters
*o++ = '^'; *o++ = '^';
*o++ = c ^ 0x40; *o++ = c ^ 0x40;
} else if (c == '@' && draw_symbols) { // Symbol??? } else if (c == '@' && draw_symbols) { // Symbol???
// Process '@' character
if (p[1] && p[1] != '@') break; if (p[1] && p[1] != '@') break;
*o++ = c; *o++ = c;
if (p[1]) p++; if (p[1]) p++;
} else { } else {
// Process regular characters
*o++ = c; *o++ = c;
} }
} }
// Calculate the final width
width = w + fl_width(word_end, (int) (o-word_end)); width = w + fl_width(word_end, (int) (o-word_end));
// Add the null terminator and set the number of characters
*o = 0; *o = 0;
n = (int)(o-buf); n = (int)(o-buf);
return p; return p;
} }
@ -124,6 +171,17 @@ static const char* expand_text_(const char* from, char*& buf, int maxbuf, double
Returns a pointer to the start of the next line of characters. Returns a pointer to the start of the next line of characters.
Sets n to the number of characters put into the buffer. Sets n to the number of characters put into the buffer.
Sets width to the width of the string in the \ref drawing_fl_font "current font". Sets width to the width of the string in the \ref drawing_fl_font "current font".
\param[in] from input text, can contain '\n' and single or double '\@' characters
\param[out] buf buffer for the output text segment
\param[in] maxbuf size of the buffer, or 0 to use the internal buffer allocated in this function
\param[in] maxw maximum width in pixels of the output text
\param[out] n number of characters in the output text segment
\param[out] width actual width in pixels of the output text
\param[in] wrap if true, wrap at maxw, else wrap at newlines
\param[in] draw_symbols if true, double '\@' characters are escaped into a single '@'
\return pointer to the next character in the input text
*/ */
const char* const char*
fl_expand_text(const char* from, char* buf, int maxbuf, double maxw, int& n, fl_expand_text(const char* from, char* buf, int maxbuf, double maxw, int& n,
@ -134,7 +192,17 @@ fl_expand_text(const char* from, char* buf, int maxbuf, double maxw, int& n,
/** /**
The same as fl_draw(const char*,int,int,int,int,Fl_Align,Fl_Image*,int) with The same as fl_draw(const char*,int,int,int,int,Fl_Align,Fl_Image*,int) with
the addition of the \p callthis parameter, which is a pointer to a text drawing the addition of the \p callthis parameter, which is a pointer to a text drawing
function such as fl_draw(const char*, int, int, int) to do the real work function such as fl_draw(const char*, int, int, int) to do the real work.
\param[in] str UTF-8 string, can start and end with an '@sym' symbol,
can contain '\n'
\param[in] x,y,w,h bounding box
\param[in] align label and image alignment in bounding box
\param[in] callthis pointer to text drawing function
\param[in] img pointer to image
\param[in] draw_symbols if true, interprete leading and trailing '@sym'
as graphical symbols
\param[in] text_to_image_spacing spacing between text and image
*/ */
void fl_draw( void fl_draw(
const char* str, // the (multi-line) string const char* str, // the (multi-line) string
@ -143,28 +211,35 @@ void fl_draw(
void (*callthis)(const char*,int,int,int), void (*callthis)(const char*,int,int,int),
Fl_Image* img, int draw_symbols) Fl_Image* img, int draw_symbols)
{ {
char *linebuf = NULL; char *linebuf = NULL; // Pointer to a buffer managed by expand_text_
const char* p; const char* p; // Scratch pointer into text, multiple use
const char* e; const char* e; // Scratch pointer into text, multiple use
int buflen; int buflen; // Number of bytes copied into linebuf
char symbol[2][255], *symptr; // by expand_text_
int symwidth[2], symoffset, symtotal, imgtotal; char symbol[2][255]; // Copy of symbol text at start and end of str
int symwidth[2]; // Width and height of symbols (always square)
int symoffset;
int symtotal; // Combined width of both sybols
int imgtotal; // Width of image if image is to the left or right
int imgvert = ((align&FL_ALIGN_IMAGE_NEXT_TO_TEXT)==0); // True if image is
// above or below text
int lines; // Number of text lines including '\n' and wrapping
double width; // width of the longest text line
int height = fl_height(); // Height of a line of text
// count how many lines and put the last one into the buffer:
int lines;
double width;
// if the image is set as a backdrop, ignore it here // Ff the image is set as a backdrop, ignore it in this function
if (img && (align & FL_ALIGN_IMAGE_BACKDROP)) img = 0; if (img && (align & FL_ALIGN_IMAGE_BACKDROP)) img = 0;
symbol[0][0] = '\0'; symbol[0][0] = '\0';
symwidth[0] = 0; symwidth[0] = 0;
symbol[1][0] = '\0'; symbol[1][0] = '\0';
symwidth[1] = 0; symwidth[1] = 0;
// Find the symbol at the start and end of the string
if (draw_symbols) { if (draw_symbols) {
if (str && str[0] == '@' && str[1] && str[1] != '@') { if (str && str[0] == '@' && str[1] && str[1] != '@') {
char *symptr;
// Start with a symbol... // Start with a symbol...
for (symptr = symbol[0]; for (symptr = symbol[0];
*str && !isspace(*str) && symptr < (symbol[0] + sizeof(symbol[0]) - 1); *str && !isspace(*str) && symptr < (symbol[0] + sizeof(symbol[0]) - 1);
@ -180,12 +255,15 @@ void fl_draw(
} }
} }
// Width and height of both symbols combined
symtotal = symwidth[0] + symwidth[1]; symtotal = symwidth[0] + symwidth[1];
// Image width if image is to the left or right of the text, else 0
imgtotal = (img && (align&FL_ALIGN_IMAGE_NEXT_TO_TEXT)) ? img->w() : 0; imgtotal = (img && (align&FL_ALIGN_IMAGE_NEXT_TO_TEXT)) ? img->w() : 0;
int strw = 0; int strw = 0; // Width of text only without symbols
int strh; int strh; // Height of text only without symbols
// Count how many lines and put the last one into the buffer:
if (str) { if (str) {
for (p = str, lines=0; p;) { for (p = str, lines=0; p;) {
e = expand_text_(p, linebuf, 0, w - symtotal - imgtotal, buflen, width, e = expand_text_(p, linebuf, 0, w - symtotal - imgtotal, buflen, width,
@ -197,61 +275,89 @@ void fl_draw(
} }
} else lines = 0; } else lines = 0;
if ((symwidth[0] || symwidth[1]) && lines) { // Fix the size of the symbols if there is at least one line of text to print
if (symwidth[0]) symwidth[0] = lines * fl_height(); if (lines) {
if (symwidth[1]) symwidth[1] = lines * fl_height(); if (symwidth[0]) symwidth[0] = lines * height;
if (symwidth[1]) symwidth[1] = lines * height;
} }
// Width and height of both symbols combined
symtotal = symwidth[0] + symwidth[1]; symtotal = symwidth[0] + symwidth[1];
// Height of text only
strh = lines * fl_height(); strh = lines * fl_height();
// figure out vertical position of the first line: // Figure out vertical position of the first element
int xpos; int xpos; // Position of image or text
int ypos; int ypos; // Position of image or text
int height = fl_height(); int imgh = img && imgvert ? img->h() : 0; // Height of image if image is above or below text
int imgvert = ((align&FL_ALIGN_IMAGE_NEXT_TO_TEXT)==0); int imgw[2] = {0, 0}; // Width of image on the left and right side of the text
int imgh = img && imgvert ? img->h() : 0;
int imgw[2] = {0, 0};
symoffset = 0; symoffset = 0;
if (align & FL_ALIGN_BOTTOM) ypos = y+h-(lines-1)*height-imgh; // Figure out vertical position of the first line of text or top image
else if (align & FL_ALIGN_TOP) ypos = y+height; if (align & FL_ALIGN_BOTTOM) {
else ypos = y+(h-lines*height-imgh)/2+height; ypos = y+h-(lines-1)*height-imgh;
} else if (align & FL_ALIGN_TOP) {
ypos = y+height;
} else {
ypos = y+(h-lines*height-imgh)/2+height;
}
// draw the image unless the "text over image" alignment flag is set... // Draw the image if located *above* the text
if (img && imgvert && !(align & FL_ALIGN_TEXT_OVER_IMAGE)) { if (img && imgvert && !(align & FL_ALIGN_TEXT_OVER_IMAGE)) {
if (img->w() > symoffset) symoffset = img->w(); if (img->w() > symoffset) symoffset = img->w();
if (align & FL_ALIGN_LEFT) xpos = x + symwidth[0]; if (align & FL_ALIGN_LEFT) {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - img->w() - symwidth[1]; xpos = x + symwidth[0];
else xpos = x + (w - img->w() - symtotal) / 2 + symwidth[0]; } else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - img->w() - symwidth[1];
} else {
xpos = x + (w - img->w() - symtotal) / 2 + symwidth[0];
}
img->draw(xpos, ypos - height); img->draw(xpos, ypos - height);
ypos += img->h(); ypos += img->h();
} }
// draw the image to the side of the text // Draw the image if either on the *left* or *right* side of the text
if (img && !imgvert /* && (align & !FL_ALIGN_TEXT_NEXT_TO_IMAGE)*/ ) { if (img && !imgvert) {
if (align & FL_ALIGN_TEXT_OVER_IMAGE) { // image is right of text if (align & FL_ALIGN_TEXT_OVER_IMAGE) {
// Image is to the right of the text
imgw[1] = img->w(); imgw[1] = img->w();
if (align & FL_ALIGN_LEFT) xpos = x + symwidth[0] + strw + 1; // Find the horizontal position of the image
else if (align & FL_ALIGN_RIGHT) xpos = x + w - symwidth[1] - imgw[1] + 1; if (align & FL_ALIGN_LEFT) {
else xpos = x + (w - strw - symtotal - imgw[1]) / 2 + symwidth[0] + strw + 1; xpos = x + symwidth[0] + strw + 1;
} else { // image is to the left of the text } else if (align & FL_ALIGN_RIGHT) {
imgw[0] = img->w(); xpos = x + w - symwidth[1] - imgw[1] + 1;
if (align & FL_ALIGN_LEFT) xpos = x + symwidth[0] - 1; } else {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - symwidth[1] - strw - imgw[0] - 1; xpos = x + (w - strw - symtotal - imgw[1]) / 2 + symwidth[0] + strw + 1;
else xpos = x + (w - strw - symtotal - imgw[0]) / 2 - 1;
} }
int yimg = ypos - height; } else {
if (align & FL_ALIGN_TOP) ; // Image is to the left of the text
else if (align & FL_ALIGN_BOTTOM) yimg += strh - img->h() - 1; imgw[0] = img->w();
else yimg += (strh - img->h() - 1) / 2; // Find the horizontal position of the image
if (align & FL_ALIGN_LEFT) {
xpos = x + symwidth[0] - 1;
} else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - symwidth[1] - strw - imgw[0] - 1;
} else {
xpos = x + (w - strw - symtotal - imgw[0]) / 2 - 1;
}
}
// Find the vertical position of the image
int yimg;
if (align & FL_ALIGN_TOP) {
yimg = ypos - height;
} else if (align & FL_ALIGN_BOTTOM) {
yimg = ypos - height + strh - img->h() - 1;
} else {
yimg = ypos - height + (strh - img->h() - 1) / 2;
}
// Draw the image
img->draw(xpos, yimg); img->draw(xpos, yimg);
} }
// now draw all the lines: // Now draw all the text lines
if (str) { if (str) {
int desc = fl_descent(); int desc = fl_descent();
for (p=str; ; ypos += height) { for (p=str; ; ypos += height) {
@ -262,9 +368,13 @@ void fl_draw(
if (width > symoffset) symoffset = (int)(width + 0.5); if (width > symoffset) symoffset = (int)(width + 0.5);
if (align & FL_ALIGN_LEFT) xpos = x + symwidth[0] + imgw[0]; if (align & FL_ALIGN_LEFT) {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - (int)(width + .5) - symwidth[1] - imgw[1]; xpos = x + symwidth[0] + imgw[0];
else xpos = x + (w - (int)(width + .5) - symtotal - imgw[0] - imgw[1]) / 2 + symwidth[0] + imgw[0]; } else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - (int)(width + .5) - symwidth[1] - imgw[1];
} else {
xpos = x + (w - (int)(width + .5) - symtotal - imgw[0] - imgw[1]) / 2 + symwidth[0] + imgw[0];
}
callthis(linebuf,buflen,xpos,ypos-desc); callthis(linebuf,buflen,xpos,ypos-desc);
@ -276,40 +386,60 @@ void fl_draw(
} }
} }
// draw the image if the "text over image" alignment flag is set... // Draw the image if the image is *below* the text
if (img && imgvert && (align & FL_ALIGN_TEXT_OVER_IMAGE)) { if (img && imgvert && (align & FL_ALIGN_TEXT_OVER_IMAGE)) {
if (img->w() > symoffset) symoffset = img->w(); if (img->w() > symoffset) symoffset = img->w();
if (align & FL_ALIGN_LEFT) xpos = x + symwidth[0]; if (align & FL_ALIGN_LEFT) {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - img->w() - symwidth[1]; xpos = x + symwidth[0];
else xpos = x + (w - img->w() - symtotal) / 2 + symwidth[0]; } else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - img->w() - symwidth[1];
} else {
xpos = x + (w - img->w() - symtotal) / 2 + symwidth[0];
}
img->draw(xpos, ypos); img->draw(xpos, ypos);
} }
// draw the symbols, if any... // Draw the symbols, if any...
if (symwidth[0]) { if (symwidth[0]) {
// draw to the left // Draw the leading symbol to the left of the text
if (align & FL_ALIGN_LEFT) xpos = x; if (align & FL_ALIGN_LEFT) {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - symtotal - symoffset; xpos = x;
else xpos = x + (w - symoffset - symtotal) / 2; } else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - symtotal - symoffset;
} else {
xpos = x + (w - symoffset - symtotal) / 2;
}
if (align & FL_ALIGN_BOTTOM) ypos = y + h - symwidth[0]; if (align & FL_ALIGN_BOTTOM) {
else if (align & FL_ALIGN_TOP) ypos = y; ypos = y + h - symwidth[0];
else ypos = y + (h - symwidth[0]) / 2; } else if (align & FL_ALIGN_TOP) {
ypos = y;
} else {
ypos = y + (h - symwidth[0]) / 2;
}
fl_draw_symbol(symbol[0], xpos, ypos, symwidth[0], symwidth[0], fl_color()); fl_draw_symbol(symbol[0], xpos, ypos, symwidth[0], symwidth[0], fl_color());
} }
if (symwidth[1]) { if (symwidth[1]) {
// draw to the right // Draw the trailing symbol to the right of the text
if (align & FL_ALIGN_LEFT) xpos = x + symoffset + symwidth[0]; if (align & FL_ALIGN_LEFT) {
else if (align & FL_ALIGN_RIGHT) xpos = x + w - symwidth[1]; xpos = x + symoffset + symwidth[0];
else xpos = x + (w - symoffset - symtotal) / 2 + symoffset + symwidth[0]; } else if (align & FL_ALIGN_RIGHT) {
xpos = x + w - symwidth[1];
} else {
xpos = x + (w - symoffset - symtotal) / 2 + symoffset + symwidth[0];
}
if (align & FL_ALIGN_BOTTOM) ypos = y + h - symwidth[1]; if (align & FL_ALIGN_BOTTOM) {
else if (align & FL_ALIGN_TOP) ypos = y; ypos = y + h - symwidth[1];
else ypos = y + (h - symwidth[1]) / 2; } else if (align & FL_ALIGN_TOP) {
ypos = y;
} else {
ypos = y + (h - symwidth[1]) / 2;
}
fl_draw_symbol(symbol[1], xpos, ypos, symwidth[1], symwidth[1], fl_color()); fl_draw_symbol(symbol[1], xpos, ypos, symwidth[1], symwidth[1], fl_color());
} }
@ -327,6 +457,15 @@ void fl_draw(
below the text as specified by the \p align value. below the text as specified by the \p align value.
The \p draw_symbols argument specifies whether or not to look for symbol The \p draw_symbols argument specifies whether or not to look for symbol
names starting with the '\@' character' names starting with the '\@' character'
\param[in] str UTF-8 string, can start and end with an '@sym' symbol,
can contain '\n'
\param[in] x,y,w,h bounding box
\param[in] align label and image alignment in bounding box
\param[in] img pointer to image
\param[in] draw_symbols if true, interprete leading and trailing '@sym'
as graphical symbols
\param[in] text_to_image_spacing spacing between text and image
*/ */
void fl_draw( void fl_draw(
const char* str, const char* str,