spmenu/libs/draw.c

522 lines
16 KiB
C
Raw Normal View History

void drawhighlights(struct item *item, int x, int y, int w, int p, const char *ittext) {
2023-05-08 23:00:45 +02:00
int i, indent;
char *highlight;
char c;
2023-03-08 19:20:18 +01:00
// limitation in order to prevent highlighting from drawing when the text isn't visible
if (columns > 5 && lines > 1) return;
char *itemtext = strdup(ittext);
2023-03-08 19:20:18 +01:00
2023-05-08 23:00:45 +02:00
if (!(strlen(itemtext) && strlen(text))) return;
2023-03-16 11:58:38 +01:00
2023-05-08 23:00:45 +02:00
drw_setscheme(drw, scheme[item == sel
? SchemeSelHighlight
: SchemeNormHighlight]);
for (i = 0, highlight = itemtext; *highlight && text[i];) {
if (((fuzzy && !fstrncmp(&(*highlight), &text[i], 1)) || (!fuzzy && *highlight == text[i]))) {
2023-05-08 23:00:45 +02:00
c = *highlight;
*highlight = '\0';
indent = TEXTW(itemtext) - lrpad;
*highlight = c;
// highlight character
c = highlight[1];
highlight[1] = '\0';
drw_text(
drw,
x + indent + (p),
y,
MIN(w - indent - lrpad, TEXTW(highlight) - lrpad),
bh, 0, highlight, 0, pango_highlight ? True : False);
highlight[1] = c;
i++;
}
highlight++;
}
2023-03-02 11:40:52 +01:00
}
int drawitemtext(struct item *item, int x, int y, int w) {
char buffer[MAXITEMLENGTH]; // buffer containing item text
Clr scm[2]; // color scheme
int leftpadding = lrpad / 2; // padding
int wr, rd; // character
2023-05-08 23:00:45 +02:00
int fg = 7; // foreground
int bg = 0; // background
int bgfg = 0; // both
int ignore = 0; // ignore colors
int selitem = 0;
int priitem = 0;
// memcpy the correct scheme
2023-03-02 11:40:52 +01:00
if (item == sel) {
memcpy(scm, scheme[SchemeItemSel], sizeof(scm));
selitem = 1;
2023-03-09 10:10:29 +01:00
if (item->hp) {
2023-03-09 10:10:29 +01:00
memcpy(scm, scheme[SchemeItemSelPri], sizeof(scm));
priitem = 1;
}
2023-03-02 11:40:52 +01:00
} else {
2023-05-13 16:07:29 +02:00
if (itemn) {
memcpy(scm, scheme[SchemeItemNorm2], sizeof(scm));
} else {
memcpy(scm, scheme[SchemeItemNorm1], sizeof(scm));
}
2023-03-09 10:10:29 +01:00
if (item->hp) {
2023-03-09 10:10:29 +01:00
memcpy(scm, scheme[SchemeItemNormPri], sizeof(scm));
priitem = 1;
}
}
// apply extra padding
if ((selitem && !priitem) && lines) {
leftpadding += selitempadding;
} else if (priitem && lines) {
leftpadding += priitempadding;
} else if (lines) {
leftpadding += normitempadding;
2023-03-02 11:40:52 +01:00
}
2023-03-28 15:10:57 +02:00
// don't color
2023-05-13 16:07:29 +02:00
if (!coloritems) memcpy(scm, scheme[SchemeItemNorm1], sizeof(scm));
2023-03-28 15:10:57 +02:00
drw_setscheme(drw, scm); // set scheme
2023-03-02 11:40:52 +01:00
// parse item text
2023-05-08 23:00:45 +02:00
for (wr = 0, rd = 0; item->text[rd]; rd++) {
if (item->text[rd] == '' && item->text[rd + 1] == '[') {
size_t alen = strspn(item->text + rd + 2, "0123456789;");
if (item->text[rd + alen + 2] == 'm' && sgr) { // last character in sequence is always 'm'
buffer[wr] = '\0';
2023-03-02 11:40:52 +01:00
if (!lines) {
w -= item->text[rd + alen];
}
apply_fribidi(buffer);
2023-05-08 23:00:45 +02:00
drw_text(drw, x, y, MIN(w, TEXTW(buffer) - lrpad) + leftpadding, bh, leftpadding, isrtl ? fribidi_text : buffer, 0, pango_item ? True : False);
drawhighlights(item, x, y, MIN(w, TEXTW(buffer) - lrpad) + leftpadding, leftpadding, isrtl ? fribidi_text : buffer);
2023-03-02 11:40:52 +01:00
// position and width
2023-05-08 23:00:45 +02:00
x += MIN(w, TEXTW(buffer) - lrpad) + leftpadding;
w -= MIN(w, TEXTW(buffer) - lrpad) + leftpadding;
2023-03-29 20:23:49 +02:00
// no highlighting if colored text
leftpadding = 0;
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
char *character = item->text + rd + 1; // current character
2023-03-02 11:40:52 +01:00
// parse hex colors in scm, m is always the last character
2023-05-08 23:00:45 +02:00
while (*character != 'm') {
unsigned nextchar = strtoul(character + 1, &character, 10);
2023-03-04 02:07:06 +01:00
if (ignore)
2023-05-08 23:00:45 +02:00
continue;
if (bgfg) {
if (bgfg < 4 && nextchar == 5) {
bgfg <<= 1;
continue;
}
if (bgfg == 4)
scm[0] = textclrs[fg = nextchar];
else if (bgfg == 6)
scm[1] = textclrs[bg = nextchar];
ignore = 1;
continue;
}
if (nextchar == 1) {
fg |= 8;
scm[0] = textclrs[fg];
} else if (nextchar == 22) {
fg &= ~8;
scm[0] = textclrs[fg];
} else if (nextchar == 38) {
2023-05-08 23:00:45 +02:00
bgfg = 2;
} else if (nextchar >= 30 && nextchar <= 37) {
fg = nextchar % 10 | (fg & 8);
scm[0] = textclrs[fg];
} else if (nextchar >= 40 && nextchar <= 47) {
bg = nextchar % 10;
scm[1] = textclrs[bg];
} else if (nextchar == 48) {
2023-03-02 11:40:52 +01:00
bgfg = 3;
} else if (nextchar == 0) {
if (item == sel) {
memcpy(scm, scheme[SchemeItemSel], sizeof(scm));
if (item->hp)
memcpy(scm, scheme[SchemeItemSelPri], sizeof(scm));
} else {
2023-05-13 16:07:29 +02:00
if (itemn) {
memcpy(scm, scheme[SchemeItemNorm2], sizeof(scm));
} else {
memcpy(scm, scheme[SchemeItemNorm1], sizeof(scm));
}
if (item->hp)
memcpy(scm, scheme[SchemeItemNormPri], sizeof(scm));
}
// don't color
2023-05-13 16:07:29 +02:00
if (!coloritems) memcpy(scm, scheme[SchemeItemNorm1], sizeof(scm));
2023-03-02 11:40:52 +01:00
}
2023-05-08 23:00:45 +02:00
}
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
rd += alen + 2;
wr = 0;
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
drw_setscheme(drw, scm); // set scheme
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
continue;
}
}
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
buffer[wr++] = item->text[rd];
}
2023-03-02 11:40:52 +01:00
2023-05-08 23:00:45 +02:00
buffer[wr] = '\0';
2023-03-02 11:40:52 +01:00
// now draw any non-colored text
2023-03-02 11:40:52 +01:00
apply_fribidi(buffer);
2023-05-08 23:00:45 +02:00
int r = drw_text(drw, x, y, w, bh, leftpadding, isrtl ? fribidi_text : buffer, 0, pango_item ? True : False);
if (!hidehighlight) drawhighlights(item, x, y, w, leftpadding, buffer);
2023-03-02 11:40:52 +01:00
// copy current buffer to item->clntext instead of item->text, this way SGR sequences aren't drawn
item->clntext = malloc(sizeof(buffer));
memcpy(item->clntext, buffer, sizeof(buffer));
2023-03-02 11:40:52 +01:00
return r;
}
int drawitem(int x, int y, int w) {
2023-05-08 23:00:45 +02:00
struct item *item;
2023-03-02 11:40:52 +01:00
int numberWidth = 0;
int modeWidth = 0;
2023-03-29 22:01:45 +02:00
int larrowWidth = 0;
int rarrowWidth = 0;
int capsWidth = 0;
// add width
2023-03-29 22:01:45 +02:00
if (!hidelarrow) larrowWidth = pango_leftarrow ? TEXTWM(leftarrow) : TEXTW(leftarrow);
if (!hiderarrow) rarrowWidth = pango_rightarrow ? TEXTWM(rightarrow) : TEXTW(rightarrow);
2023-03-02 18:25:28 +01:00
if (!hidemode) modeWidth = pango_mode ? TEXTWM(modetext) : TEXTW(modetext);
if (!hiderarrow) rarrowWidth = pango_rightarrow ? TEXTWM(rightarrow) : TEXTW(rightarrow);
2023-03-26 16:42:11 +02:00
if (!hidematchcount) numberWidth = pango_numbers ? TEXTWM(numbers) : TEXTW(numbers);
if (!hidecaps) capsWidth = pango_caps ? TEXTWM(capstext) : TEXTW(capstext);
2023-03-12 21:03:35 +01:00
2023-04-21 09:49:38 +02:00
if (!strcmp(capstext, ""))
capsWidth = 0;
2023-03-12 21:03:35 +01:00
2023-05-08 23:00:45 +02:00
#if USEIMAGE
2023-03-26 16:42:11 +02:00
int ox = 0; // original x position
2023-05-08 23:00:45 +02:00
#endif
2023-03-02 11:40:52 +01:00
// draw items and image
2023-05-08 23:00:45 +02:00
if (lines > 0) {
int i = 0;
2023-03-24 04:04:37 +01:00
int rx = 0;
// draw image first
2023-05-08 23:00:45 +02:00
#if USEIMAGE
2023-03-02 16:22:59 +01:00
if (!hideimage && longestedge != 0) {
2023-03-24 04:04:37 +01:00
rx = ox;
rx += MAX((imagegaps * 2) + imagewidth + menumarginh, indentitems ? x : 0);
2023-03-24 04:04:37 +01:00
} else
2023-05-08 23:00:45 +02:00
#endif
2023-03-24 04:04:37 +01:00
if (!indentitems) {
rx = 0;
} else {
rx = x;
}
2023-05-07 15:33:09 +02:00
int itemoverride = 1;
itemn = 0;
2023-05-07 15:33:09 +02:00
2023-05-08 23:00:45 +02:00
for (item = curr; item != next; item = item->right, i++) {
x = drawitemtext(
item,
rx + ((i / lines) * ((mw - rx) / columns)),
y + (((i % lines) + 1) * bh),
(mw - rx) / columns
);
2023-05-07 15:33:09 +02:00
if (item == sel && itemoverride) {
itemnumber = i;
itemoverride = 0;
}
2023-05-13 16:07:29 +02:00
itemn = !itemn;
2023-03-13 22:45:04 +01:00
}
2023-05-08 23:00:45 +02:00
// horizontal list
} else if (matches) {
x += inputw;
2023-04-05 19:07:29 +02:00
w = larrowWidth;
x = drawlarrow(x, y, w);
2023-05-07 15:33:09 +02:00
itemnumber = 0;
int itemoverride = 1;
2023-05-08 23:00:45 +02:00
for (item = curr; item != next; item = item->right) { // draw items
2023-04-21 09:49:38 +02:00
x = drawitemtext(item, x, y, MIN(pango_item ? TEXTWM(item->text) : TEXTW(item->text),
mw - x -
rarrowWidth -
numberWidth -
modeWidth -
capsWidth -
menumarginh -
2 * sp -
2 * borderwidth
2023-05-08 23:00:45 +02:00
));
2023-04-21 09:49:38 +02:00
2023-05-07 15:33:09 +02:00
if (itemoverride) {
itemnumber++;
}
if (item == sel) {
itemoverride = 0;
}
}
w = rarrowWidth + numberWidth + modeWidth + capsWidth + menumarginh + 2 * sp + 2 * borderwidth;
x = drawrarrow(mw - w, y, w);
2023-05-08 23:00:45 +02:00
}
2023-03-02 11:40:52 +01:00
2023-03-26 16:42:11 +02:00
return x;
}
int drawprompt(int x, int y, int w) {
2023-05-08 23:00:45 +02:00
if (prompt && *prompt && !hideprompt) {
drw_setscheme(drw, scheme[SchemePrompt]);
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
x = drw_text(drw, x, y, w, bh, lrpad / 2, prompt, 0, pango_prompt ? True : False);
2023-03-26 16:42:11 +02:00
if (!hidepowerline && powerlineprompt) {
2023-03-26 16:42:11 +02:00
drw_settrans(drw, scheme[SchemePrompt], scheme[SchemeMenu]);
drw_arrow(drw, x, y, plw, bh, 1, promptpwlstyle);
2023-03-26 16:42:11 +02:00
x += plw;
}
2023-05-08 23:00:45 +02:00
}
2023-03-26 16:42:11 +02:00
return x;
}
int drawinput(int x, int y, int w) {
2023-05-08 23:00:45 +02:00
char *censort; // censor text (password)
unsigned int curpos = 0;
2023-03-26 16:42:11 +02:00
int fh = drw->font->h;
// draw input
2023-05-08 23:00:45 +02:00
drw_setscheme(drw, scheme[SchemeInput]);
2023-04-05 18:58:48 +02:00
2023-05-08 23:00:45 +02:00
if (passwd) {
censort = ecalloc(1, sizeof(text));
for (int i = 0; i < strlen(text); i++)
memcpy(&censort[i], password, strlen(text));
2023-03-26 16:42:11 +02:00
apply_fribidi(censort);
2023-05-08 23:00:45 +02:00
drw_text(drw, x, y, w, bh, lrpad / 2, isrtl ? fribidi_text : censort, 0, pango_password ? True : False);
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
curpos = TEXTW(censort) - TEXTW(&text[cursor]);
2023-03-26 16:42:11 +02:00
free(censort);
2023-05-08 23:00:45 +02:00
} else if (!passwd) {
2023-03-26 16:42:11 +02:00
apply_fribidi(text);
drw_text(drw, x, y, w, bh, lrpad / 2, isrtl ? fribidi_text : text, 0, pango_input ? True : False);
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
curpos = TEXTW(text) - TEXTW(&text[cursor]);
2023-03-26 16:42:11 +02:00
}
2023-05-08 23:00:45 +02:00
if ((curpos += lrpad / 2 - 1) < w && !hidecaret && cursorstate) {
drw_setscheme(drw, scheme[SchemeCaret]);
drw_rect(drw, x + curpos, 2 + (bh - fh) / 2 + y, 2, fh - 4, 1, 0);
}
2023-03-26 16:42:11 +02:00
return x;
}
int drawlarrow(int x, int y, int w) {
2023-03-29 22:01:45 +02:00
if (hidelarrow) return x;
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
if (curr->left) { // draw left arrow
drw_setscheme(drw, scheme[SchemeLArrow]);
drw_text(drw, x, y, w, bh, lrpad / 2, leftarrow, 0, pango_leftarrow ? True : False);
x += w;
}
2023-03-26 16:42:11 +02:00
return x;
}
int drawrarrow(int x, int y, int w) {
2023-03-29 22:01:45 +02:00
if (hiderarrow) return x;
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
if (next) { // draw right arrow
drw_setscheme(drw, scheme[SchemeRArrow]);
2023-04-05 19:07:29 +02:00
drw_text(drw, mw - w, y, w, bh, lrpad / 2, rightarrow, 0, pango_rightarrow ? True : False);
2023-03-29 22:01:45 +02:00
x += w;
2023-05-08 23:00:45 +02:00
}
2023-03-02 11:40:52 +01:00
2023-03-26 16:42:11 +02:00
return x;
}
int drawnumber(int x, int y, int w) {
2023-04-05 19:07:29 +02:00
if (hidematchcount) return x;
2023-03-21 16:40:11 +01:00
int powerlinewidth = 0;
if (!hidepowerline && powerlinecount) {
powerlinewidth = plw / 2;
}
2023-03-21 16:40:11 +01:00
drw_setscheme(drw, scheme[SchemeNumber]);
drw_text(drw, x, y, w, bh, lrpad / 2 + powerlinewidth, numbers, 0, pango_numbers ? True : False);
2023-03-21 16:40:11 +01:00
2023-04-05 19:07:29 +02:00
// draw powerline for match count
if (!hidepowerline && powerlinecount) {
2023-04-05 19:07:29 +02:00
drw_settrans(drw, scheme[SchemeNumber], scheme[SchemeMenu]);
drw_arrow(drw, x, y, plw, bh, 0, matchcountpwlstyle);
2023-04-05 19:07:29 +02:00
x += plw;
2023-03-02 11:40:52 +01:00
}
2023-03-26 16:42:11 +02:00
return x;
}
int drawmode(int x, int y, int w) {
if (!hidemode) { // draw mode indicator
int powerlinewidth = 0;
if (!hidepowerline && powerlinemode) {
powerlinewidth = plw / 2;
}
2023-03-21 16:40:11 +01:00
drw_setscheme(drw, scheme[SchemeMode]);
drw_text(drw, x, y, w, bh, lrpad / 2 + powerlinewidth, modetext, 0, pango_mode ? True : False);
2023-03-21 16:40:11 +01:00
// draw powerline for match count
if (!hidepowerline && powerlinemode) {
2023-03-21 16:40:11 +01:00
drw_settrans(drw, scheme[SchemeMode], hidematchcount ? scheme[SchemeMenu] : scheme[SchemeNumber]);
drw_arrow(drw, x, y, plw, bh, 0, modepwlstyle);
2023-03-21 16:40:11 +01:00
x += plw;
}
2023-03-02 11:40:52 +01:00
}
2023-05-08 23:00:45 +02:00
return x;
2023-03-26 16:42:11 +02:00
}
int drawcaps(int x, int y, int w) {
if (!w) return x; // not caps lock
if (!hidecaps) { // draw caps lock indicator
int powerlinewidth = 0;
if (!hidepowerline && powerlinecaps) {
powerlinewidth = plw / 2;
}
drw_setscheme(drw, scheme[SchemeCaps]);
drw_text(drw, x, y, w, bh, lrpad / 2 + powerlinewidth, capstext, 0, pango_caps ? True : False);
// draw powerline for caps lock indicator
if (!hidepowerline && powerlinecaps) {
drw_settrans(drw, scheme[SchemeCaps], hidemode ? hidematchcount ? scheme[SchemeMenu] : scheme[SchemeNumber] : scheme[SchemeMode]);
drw_arrow(drw, x, y, plw, bh, 0, capspwlstyle);
x += plw;
}
}
2023-05-08 23:00:45 +02:00
return x;
}
void drawmenu(void) {
2023-05-08 23:00:45 +02:00
int x = 0, y = 0, w = 0;
2023-03-26 16:42:11 +02:00
plw = hidepowerline ? 0 : drw->font->h / 2 + 1; // powerline size
// draw menu first using menu scheme
2023-05-08 23:00:45 +02:00
drw_setscheme(drw, scheme[SchemeMenu]);
drw_rect(drw, 0, 0, mw, mh, 1, 1);
2023-03-26 16:42:11 +02:00
int numberWidth = 0;
int modeWidth = 0;
int capsWidth = 0;
2023-03-26 16:42:11 +02:00
// add width
if (!hidemode) modeWidth = pango_mode ? TEXTWM(modetext) : TEXTW(modetext);
if (!hidecaps) capsWidth = pango_caps ? TEXTWM(capstext) : TEXTW(capstext);
if (!strcmp(capstext, ""))
capsWidth = 0;
2023-03-26 16:42:11 +02:00
// calculate match count
if (!hidematchcount) {
recalculatenumbers();
numberWidth = TEXTW(numbers);
}
x += menumarginh;
y += menumarginv;
2023-04-21 09:49:38 +02:00
calcoffsets();
// why have an empty line?
if ((hideprompt && hideinput && hidemode && hidematchcount && hidecaps
2023-05-08 23:00:45 +02:00
#if USEIMAGE
2023-04-01 15:03:42 +02:00
) && (!image || hideimage)) {
2023-05-08 23:00:45 +02:00
#else
)) {
2023-05-08 23:00:45 +02:00
#endif
y -= bh;
mh = (lines + 1) * bh - bh + 2 * menumarginv;
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
if (!win) return;
2023-05-08 23:00:45 +02:00
XResizeWindow(dpy, win, mw - 2 * sp - 2 * borderwidth, mh);
drw_resize(drw, mw - 2 * sp - 2 * borderwidth, mh);
}
#if USEIMAGE
else if (hideprompt && hideinput && hidemode && hidematchcount) {
y -= bh;
}
#endif
2023-05-08 23:00:45 +02:00
if (!hideprompt) {
w = promptw;
x = drawprompt(x, y, w);
}
if (!hideinput) {
w = (lines > 0 || !matches) ? mw - x : inputw;
x = drawinput(x, y, w);
}
2023-04-05 18:58:48 +02:00
2023-05-08 23:00:45 +02:00
if (!hidemode) modeWidth = pango_mode ? TEXTWM(modetext) : TEXTW(modetext);
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
// draw the items, this function also calls drawrarrow() and drawlarrow()
if (!hideitem) drawitem(x, y, w);
2023-05-08 23:00:45 +02:00
if (!hidematchcount) {
w = numberWidth;
drawnumber(mw - numberWidth - modeWidth - capsWidth - 2 * sp - 2 * borderwidth - menumarginh, y, w);
}
2023-04-05 19:07:29 +02:00
2023-05-08 23:00:45 +02:00
if (!hidemode) {
w = modeWidth;
drawmode(mw - modeWidth - capsWidth - 2 * sp - 2 * borderwidth - menumarginh, y, w);
}
2023-05-08 23:00:45 +02:00
if (!hidecaps) {
w = capsWidth;
drawcaps(mw - capsWidth - 2 * sp - 2 * borderwidth - menumarginh, y, w);
}
2023-03-26 16:42:11 +02:00
2023-05-08 23:00:45 +02:00
drw_map(drw, win, 0, 0, mw, mh);
}