summaryrefslogtreecommitdiff
path: root/lib/renderers/cairo.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/renderers/cairo.h')
-rw-r--r--lib/renderers/cairo.h171
1 files changed, 118 insertions, 53 deletions
diff --git a/lib/renderers/cairo.h b/lib/renderers/cairo.h
index d9ba093..3fc1c90 100644
--- a/lib/renderers/cairo.h
+++ b/lib/renderers/cairo.h
@@ -4,19 +4,14 @@
#include "internal.h"
#include <string.h>
#include <assert.h>
+#include <math.h>
#include <cairo/cairo.h>
-
-#ifndef MAX
-# define MAX(a,b) (((a)>(b))?(a):(b))
-#endif
-
-#ifndef MIN
-# define MIN(a,b) (((a)<(b))?(a):(b))
-#endif
+#include <pango/pangocairo.h>
struct cairo {
cairo_t *cr;
cairo_surface_t *surface;
+ PangoContext *pango;
};
struct cairo_color {
@@ -26,7 +21,7 @@ struct cairo_color {
struct cairo_paint {
struct cairo_color fg;
struct cairo_color bg;
- cairo_font_extents_t fe;
+ const char *font;
struct box {
int32_t lx, rx; // left/right offset (pos.x - lx, box.w + rx)
@@ -40,25 +35,62 @@ struct cairo_paint {
};
struct cairo_result {
- cairo_text_extents_t te;
+ uint32_t x_advance;
+ uint32_t height;
+};
+
+struct cairo_paint_result {
+ uint32_t displayed;
+ uint32_t height;
};
static size_t blen = 0;
static char *buffer = NULL;
+__attribute__((unused)) static bool
+bm_cairo_create_for_surface(struct cairo *cairo, cairo_surface_t *surface)
+{
+ assert(cairo && surface);
+ if (!(cairo->cr = cairo_create(surface)))
+ goto fail;
+
+ if (!(cairo->pango = pango_cairo_create_context(cairo->cr)))
+ goto fail;
+
+ cairo->surface = surface;
+ return true;
+
+fail:
+ if (cairo->cr)
+ cairo_destroy(cairo->cr);
+ return false;
+}
+
__attribute__((unused)) static void
-bm_cairo_get_font_extents(struct cairo *cairo, const struct bm_font *font, cairo_font_extents_t *fe)
+bm_cairo_destroy(struct cairo *cairo)
{
- assert(cairo && font && fe);
- cairo_select_font_face(cairo->cr, font->name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
- cairo_set_font_size(cairo->cr, font->size);
- cairo_font_extents(cairo->cr, fe);
+ if (cairo->cr)
+ cairo_destroy(cairo->cr);
+ if (cairo->surface)
+ cairo_surface_destroy(cairo->surface);
}
-__attribute__((unused)) BM_LOG_ATTR(3, 4) static bool
-bm_cairo_get_text_extents(struct cairo *cairo, struct cairo_result *result, const char *fmt, ...)
+__attribute__((unused)) static PangoLayout*
+bm_pango_get_layout(struct cairo *cairo, struct cairo_paint *paint, const char *buffer)
{
- assert(cairo && result && fmt);
+ PangoLayout *layout = pango_cairo_create_layout(cairo->cr);
+ pango_layout_set_text(layout, buffer, -1);
+ PangoFontDescription *desc = pango_font_description_from_string(paint->font);
+ pango_layout_set_font_description(layout, desc);
+ pango_layout_set_single_paragraph_mode(layout, 1);
+ pango_font_description_free(desc);
+ return layout;
+}
+
+__attribute__((unused)) BM_LOG_ATTR(4, 5) static bool
+bm_pango_get_text_extents(struct cairo *cairo, struct cairo_paint *paint, struct cairo_result *result, const char *fmt, ...)
+{
+ assert(cairo && paint && result && fmt);
memset(result, 0, sizeof(struct cairo_result));
va_list args;
@@ -69,7 +101,13 @@ bm_cairo_get_text_extents(struct cairo *cairo, struct cairo_result *result, cons
if (!ret)
return false;
- cairo_text_extents(cairo->cr, buffer, &result->te);
+ PangoRectangle rect;
+ PangoLayout *layout = bm_pango_get_layout(cairo, paint, buffer);
+ pango_layout_get_pixel_extents(layout, &rect, NULL);
+ g_object_unref(layout);
+
+ result->x_advance = rect.x + rect.width;
+ result->height = rect.height;
return true;
}
@@ -87,22 +125,29 @@ bm_cairo_draw_line(struct cairo *cairo, struct cairo_paint *paint, struct cairo_
if (!ret)
return false;
- cairo_text_extents_t te;
- cairo_text_extents(cairo->cr, buffer, &te);
+ PangoLayout *layout = bm_pango_get_layout(cairo, paint, buffer);
+ pango_cairo_update_layout(cairo->cr, layout);
+
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+ int base = pango_layout_get_baseline(layout) / PANGO_SCALE;
+ int yoff = height - base;
cairo_set_source_rgba(cairo->cr, paint->bg.r, paint->bg.b, paint->bg.g, paint->bg.a);
cairo_rectangle(cairo->cr,
paint->pos.x - paint->box.lx, paint->pos.y - paint->box.ty,
- (paint->box.w > 0 ? paint->box.w : te.width) + paint->box.rx + paint->box.lx,
- (paint->box.h > 0 ? paint->box.h : paint->fe.height) + paint->box.by + paint->box.ty);
+ (paint->box.w > 0 ? paint->box.w : width) + paint->box.rx + paint->box.lx,
+ (paint->box.h > 0 ? paint->box.h : height) + paint->box.by + paint->box.ty);
cairo_fill(cairo->cr);
cairo_set_source_rgba(cairo->cr, paint->fg.r, paint->fg.b, paint->fg.g, paint->fg.a);
- cairo_move_to(cairo->cr, paint->box.lx + paint->pos.x, paint->pos.y + paint->fe.descent + paint->fe.height * 0.5 + paint->box.ty);
- cairo_show_text(cairo->cr, buffer);
+ cairo_move_to(cairo->cr, paint->box.lx + paint->pos.x, paint->pos.y - yoff + paint->box.ty);
+ pango_cairo_show_layout(cairo->cr, layout);
- te.x_advance += paint->box.rx;
- memcpy(&result->te, &te, sizeof(te));
+ g_object_unref(layout);
+
+ result->x_advance = width + paint->box.rx;
+ result->height = height + paint->box.by;
return true;
}
@@ -116,10 +161,16 @@ bm_cairo_color_from_menu_color(const struct bm_menu *menu, enum bm_color color,
c->a = 1.0f;
}
-__attribute__((unused)) static uint32_t
-bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struct bm_menu *menu)
+__attribute__((unused)) static void
+bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, uint32_t max_height, const struct bm_menu *menu, struct cairo_paint_result *out_result)
{
- assert(cairo && menu);
+ assert(cairo && menu && out_result);
+
+ memset(out_result, 0, sizeof(struct cairo_paint_result));
+ out_result->displayed = 1;
+
+ char font[128];
+ snprintf(font, sizeof(font), "%s %d", menu->font.name, menu->font.size);
struct cairo_color c;
bm_cairo_color_from_menu_color(menu, BM_COLOR_BG, &c);
@@ -129,37 +180,50 @@ bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struc
struct cairo_paint paint;
memset(&paint, 0, sizeof(paint));
- bm_cairo_get_font_extents(cairo, &menu->font, &paint.fe);
+ paint.font = font;
struct cairo_result result;
memset(&result, 0, sizeof(result));
+ uint32_t title_x = 0;
if (menu->title) {
bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_FG, &paint.fg);
bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_BG, &paint.bg);
- paint.pos = (struct pos){ result.te.x_advance, 2 };
+ paint.pos = (struct pos){ result.x_advance, 2 };
paint.box = (struct box){ 4, 8, 2, 2, 0, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "%s", menu->title);
+ title_x = result.x_advance;
}
bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg);
bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg);
- paint.pos = (struct pos){ (menu->title ? 2 : 0) + result.te.x_advance, 2 };
+ paint.pos = (struct pos){ (menu->title ? 2 : 0) + result.x_advance, 2 };
paint.box = (struct box){ (menu->title ? 2 : 4), 0, 2, 2, width - paint.pos.x, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "%s", (menu->filter ? menu->filter : ""));
+ out_result->height = result.height;
+
+ uint32_t count;
+ struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
+ uint32_t lines = (menu->lines > 0 ? menu->lines : 1);
- uint32_t displayed = 1;
- uint32_t lines = MAX(height / (paint.fe.height + 4), 1);
if (lines > 1) {
+ /* vertical mode */
+
+ uint32_t spacing = 0; // 0 == variable width spacing
+ if (lines > max_height / result.height) {
+ /* there is more lines than screen can fit, enter fixed spacing mode */
+ lines = max_height / result.height - 1;
+ spacing = result.height;
+ }
+
uint32_t start_x = 0;
if (menu->prefix) {
- bm_cairo_get_text_extents(cairo, &result, "%s ", menu->prefix);
- start_x = result.te.x_advance;
+ bm_pango_get_text_extents(cairo, &paint, &result, "%s ", menu->prefix);
+ start_x = result.x_advance;
}
- uint32_t count, cl = 1;
- struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
- for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) {
+ uint32_t posy = out_result->height;
+ for (uint32_t l = 0, i = (menu->index / lines) * lines; l < lines && i < count && posy < max_height; ++i, ++l) {
bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu));
if (highlighted) {
@@ -174,27 +238,29 @@ bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struc
}
if (menu->prefix && highlighted) {
- paint.pos = (struct pos){ 0, 2 + (paint.fe.height + 4) * cl++ };
+ paint.pos = (struct pos){ 0, 2 + posy };
paint.box = (struct box){ 4, 0, 2, 2, width - paint.pos.x, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "%s %s", menu->prefix, (items[i]->text ? items[i]->text : ""));
} else {
- paint.pos = (struct pos){ 0, 2 + (paint.fe.height + 4) * cl++ };
+ paint.pos = (struct pos){ 0, 2 + posy };
paint.box = (struct box){ 4 + start_x, 0, 2, 2, width - paint.pos.x, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "%s", (items[i]->text ? items[i]->text : ""));
}
- ++displayed;
+ posy += (spacing ? spacing : result.height);
+ out_result->height = posy + 2;
+ out_result->displayed++;
}
} else {
- uint32_t count;
- struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
+ /* single-line mode */
+ bm_pango_get_text_extents(cairo, &paint, &result, "lorem ipsum lorem ipsum lorem ipsum lorem");
+ uint32_t cl = fmin(title_x + result.x_advance, width / 4);
- uint32_t cl = width / 4;
if (menu->wrap || menu->index > 0) {
paint.pos = (struct pos){ cl, 2 };
paint.box = (struct box){ 1, 2, 2, 2, 0, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "<");
- cl += result.te.x_advance + 1;
+ cl += result.x_advance + 1;
}
for (uint32_t i = menu->index; i < count && cl < width; ++i) {
@@ -214,21 +280,20 @@ bm_cairo_paint(struct cairo *cairo, uint32_t width, uint32_t height, const struc
paint.pos = (struct pos){ cl, 2 };
paint.box = (struct box){ 2, 4, 2, 2, 0, 0 };
bm_cairo_draw_line(cairo, &paint, &result, "%s", (items[i]->text ? items[i]->text : ""));
- cl += result.te.x_advance + 2;
- displayed += (cl < width);
+ cl += result.x_advance + 2;
+ out_result->displayed += (cl < width);
+ out_result->height = fmax(out_result->height, result.height);
}
if (menu->wrap || menu->index + 1 < count) {
bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg);
bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg);
- bm_cairo_get_text_extents(cairo, &result, ">");
- paint.pos = (struct pos){ width - result.te.x_advance - 2, 2 };
+ bm_pango_get_text_extents(cairo, &paint, &result, ">");
+ paint.pos = (struct pos){ width - result.x_advance - 2, 2 };
paint.box = (struct box){ 1, 2, 2, 2, 0, 0 };
bm_cairo_draw_line(cairo, &paint, &result, ">");
}
}
-
- return displayed;
}
#endif /* _BM_CAIRO_H */