summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--completions/tofi1
-rw-r--r--doc/config3
-rw-r--r--doc/tofi.5.md8
-rw-r--r--doc/tofi.5.scd7
-rw-r--r--src/config.c2
-rw-r--r--src/entry.h1
-rw-r--r--src/entry_backend/harfbuzz.c87
-rw-r--r--src/entry_backend/pango.c52
-rw-r--r--src/main.c3
9 files changed, 149 insertions, 15 deletions
diff --git a/completions/tofi b/completions/tofi
index 5942cf7..add8aab 100644
--- a/completions/tofi
+++ b/completions/tofi
@@ -16,6 +16,7 @@ _tofi()
--font-size
--num-results
--selection-color
+ --selection-match-color
--selection-padding
--selection-background
--outline-width
diff --git a/doc/config b/doc/config
index eb360e4..454f275 100644
--- a/doc/config
+++ b/doc/config
@@ -42,6 +42,9 @@
# Selection text
selection-color = #F92672
+ # Matching portion of selection text
+ selection-match-color = #00000000
+
# Selection background
selection-background = #00000000
diff --git a/doc/tofi.5.md b/doc/tofi.5.md
index 539b525..c922fac 100644
--- a/doc/tofi.5.md
+++ b/doc/tofi.5.md
@@ -94,6 +94,14 @@ options.
>
> Default: \#F92672
+**selection-match-color**=*color*
+
+> Color of the matching portion of the selected result. Any color that
+> is fully transparent (alpha = 0) will disable this highlighting. See
+> **COLORS** for more information.
+>
+> Default: \#00000000
+
**selection-padding**=*px*
> Extra horizontal padding of the selection background. If *px* = -1,
diff --git a/doc/tofi.5.scd b/doc/tofi.5.scd
index 8341200..3993e78 100644
--- a/doc/tofi.5.scd
+++ b/doc/tofi.5.scd
@@ -85,6 +85,13 @@ options.
Default: #F92672
+*selection-match-color*=_color_
+ Color of the matching portion of the selected result. Any color that is
+ fully transparent (alpha = 0) will disable this highlighting.
+ See *COLORS* for more information.
+
+ Default: #00000000
+
*selection-padding*=_px_
Extra horizontal padding of the selection background. If _px_ = -1,
the padding will fill the whole window width.
diff --git a/src/config.c b/src/config.c
index a0e77b2..8cb3763 100644
--- a/src/config.c
+++ b/src/config.c
@@ -263,6 +263,8 @@ bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const
tofi->window.entry.foreground_color = parse_color(filename, lineno, value, &err);
} else if (strcasecmp(option, "selection-color") == 0) {
tofi->window.entry.selection_foreground_color = parse_color(filename, lineno, value, &err);
+ } else if (strcasecmp(option, "selection-match-color") == 0) {
+ tofi->window.entry.selection_highlight_color = parse_color(filename, lineno, value, &err);
} else if (strcasecmp(option, "selection-padding") == 0) {
tofi->window.entry.selection_background_padding = parse_int32(filename, lineno, value, &err);
} else if (strcasecmp(option, "selection-background") == 0) {
diff --git a/src/entry.h b/src/entry.h
index 5536e06..562c085 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -65,6 +65,7 @@ struct entry {
uint32_t outline_width;
struct color foreground_color;
struct color background_color;
+ struct color selection_highlight_color;
struct color selection_foreground_color;
struct color selection_background_color;
struct color border_color;
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c
index cba1fd8..e3217a7 100644
--- a/src/entry_backend/harfbuzz.c
+++ b/src/entry_backend/harfbuzz.c
@@ -242,17 +242,88 @@ void entry_backend_harfbuzz_update(struct entry *entry)
break;
}
- hb_buffer_clear_contents(buffer);
- setup_hb_buffer(buffer);
- hb_buffer_add_utf8(buffer, entry->results.buf[i].string, -1, 0, -1);
- hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
- if (i == entry->selection) {
+ /* If this isn't the selected result, just print as normal. */
+ if (i != entry->selection) {
+ hb_buffer_clear_contents(buffer);
+ setup_hb_buffer(buffer);
+ hb_buffer_add_utf8(buffer, entry->results.buf[i].string, -1, 0, -1);
+ hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
+ width = render_hb_buffer(cr, buffer);
+ } else {
+ /*
+ * For the selected result, there's a bit more to do.
+ *
+ * First, we need to use a different foreground color -
+ * simple enough.
+ *
+ * Next, we may need to draw a background box - this
+ * involves rendering to a cairo group, measuring the
+ * size of the text, drawing the background on the main
+ * canvas, then finally drawing the group on top of
+ * that.
+ *
+ * Finally, we may need to highlight the matching
+ * portion of text - this is achieved simply by
+ * splitting the text into prematch, match and
+ * postmatch chunks, and drawing each separately.
+ */
+ size_t prematch_len;
+ char *prematch = xstrdup(entry->results.buf[i].string);
+ char *match = NULL;
+ char *postmatch = NULL;
+ uint32_t subwidth;
+ if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) {
+ char *match_pos = strcasestr(prematch, entry->input_mb);
+ if (match_pos != NULL) {
+ match = xstrdup(entry->results.buf[i].string);
+ postmatch = xstrdup(entry->results.buf[i].string);
+ prematch_len = (match_pos - prematch);
+ prematch[prematch_len] = '\0';
+ match[entry->input_mb_length + prematch_len] = '\0';
+ }
+ }
+
cairo_push_group(cr);
color = entry->selection_foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- }
- width = render_hb_buffer(cr, buffer);
- if (i == entry->selection) {
+
+ hb_buffer_clear_contents(buffer);
+ setup_hb_buffer(buffer);
+ hb_buffer_add_utf8(buffer, prematch, -1, 0, -1);
+ hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
+ subwidth = render_hb_buffer(cr, buffer);
+ width = subwidth;
+
+ if (match != NULL) {
+ cairo_translate(cr, subwidth, 0);
+ color = entry->selection_highlight_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ hb_buffer_clear_contents(buffer);
+ setup_hb_buffer(buffer);
+ hb_buffer_add_utf8(buffer, &match[prematch_len], -1, 0, -1);
+ hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
+ subwidth = render_hb_buffer(cr, buffer);
+ width += subwidth;
+
+ cairo_translate(cr, subwidth, 0);
+ color = entry->selection_foreground_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ hb_buffer_clear_contents(buffer);
+ setup_hb_buffer(buffer);
+ hb_buffer_add_utf8(buffer, &postmatch[entry->input_mb_length + prematch_len], -1, 0, -1);
+ hb_shape(entry->harfbuzz.hb_font, buffer, NULL, 0);
+ subwidth = render_hb_buffer(cr, buffer);
+ width += subwidth;
+
+ free(match);
+ free(postmatch);
+ match = NULL;
+ postmatch = NULL;
+ }
+
+ free(prematch);
+ prematch = NULL;
+
cairo_pop_group_to_source(cr);
cairo_save(cr);
color = entry->selection_background_color;
diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c
index 73eeb49..c182e4c 100644
--- a/src/entry_backend/pango.c
+++ b/src/entry_backend/pango.c
@@ -4,6 +4,7 @@
#include "../entry.h"
#include "../log.h"
#include "../nelem.h"
+#include "../xmalloc.h"
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -95,16 +96,53 @@ void entry_backend_pango_update(struct entry *entry)
} else {
str = "";
}
- pango_layout_set_text(layout, str, -1);
- pango_cairo_update_layout(cr, layout);
- if (i == entry->selection) {
+ if (i != entry->selection) {
+ pango_layout_set_text(layout, str, -1);
+ pango_cairo_update_layout(cr, layout);
+ pango_cairo_show_layout(cr, layout);
+ pango_layout_get_size(layout, &width, &height);
+ } else {
+ ssize_t prematch_len = -1;
+ size_t match_len = entry->input_mb_length;
+ int32_t subwidth;
+ if (entry->input_mb_length > 0 && entry->selection_highlight_color.a != 0) {
+ char *match_pos = strcasestr(str, entry->input_mb);
+ if (match_pos != NULL) {
+ prematch_len = (match_pos - str);
+ }
+ }
+
cairo_push_group(cr);
color = entry->selection_foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- }
- pango_cairo_show_layout(cr, layout);
- pango_layout_get_size(layout, &width, &height);
- if (i == entry->selection) {
+
+ pango_layout_set_text(layout, str, prematch_len);
+ pango_cairo_update_layout(cr, layout);
+ pango_cairo_show_layout(cr, layout);
+ pango_layout_get_size(layout, &subwidth, &height);
+ width = subwidth;
+
+ if (prematch_len != -1) {
+ cairo_translate(cr, (int)(subwidth / PANGO_SCALE), 0);
+ color = entry->selection_highlight_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ pango_layout_set_text(layout, &str[prematch_len], match_len);
+ pango_cairo_update_layout(cr, layout);
+ pango_cairo_show_layout(cr, layout);
+ pango_layout_get_size(layout, &subwidth, &height);
+ width += subwidth;
+
+ cairo_translate(cr, (int)(subwidth / PANGO_SCALE), 0);
+ color = entry->selection_foreground_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ pango_layout_set_text(layout, &str[prematch_len + match_len], -1);
+ pango_cairo_update_layout(cr, layout);
+ pango_cairo_show_layout(cr, layout);
+ pango_layout_get_size(layout, &subwidth, &height);
+ width += subwidth;
+
+ }
+
cairo_pop_group_to_source(cr);
cairo_save(cr);
color = entry->selection_background_color;
diff --git a/src/main.c b/src/main.c
index 9a3ace1..4c5f497 100644
--- a/src/main.c
+++ b/src/main.c
@@ -687,6 +687,8 @@ static void usage()
" --prompt-text <string> Prompt text.\n"
" --num-results <n> Maximum number of results to display.\n"
" --selection-color <color> Color of selected result.\n"
+" --selection-match-color <color> Color of the matching portion of the\n"
+" selected result.\n"
" --selection-padding <px> Extra horizontal padding for selected\n"
" result background.\n"
" --selection-background <color> Color of selected result background.\n"
@@ -729,6 +731,7 @@ const struct option long_options[] = {
{"font-size", required_argument, NULL, 0},
{"num-results", required_argument, NULL, 0},
{"selection-color", required_argument, NULL, 0},
+ {"selection-match-color", required_argument, NULL, 0},
{"selection-padding", required_argument, NULL, 0},
{"selection-background", required_argument, NULL, 0},
{"outline-width", required_argument, NULL, 0},