8 #include <cairo/cairo.h>
9 #include <pango/pangocairo.h>
13 cairo_surface_t *surface;
47 static size_t blen = 0;
48 static char *
buffer = NULL;
50 __attribute__((unused)) static
bool
51 bm_cairo_create_for_surface(struct
cairo *
cairo, cairo_surface_t *surface)
53 assert(cairo && surface);
54 if (!(cairo->cr = cairo_create(surface)))
57 if (!(cairo->pango = pango_cairo_create_context(cairo->cr)))
60 cairo->surface = surface;
65 cairo_destroy(cairo->cr);
69 __attribute__((unused)) static
void
70 bm_cairo_destroy(struct cairo *cairo)
73 cairo_destroy(cairo->cr);
75 cairo_surface_destroy(cairo->surface);
78 __attribute__((unused)) static PangoLayout*
79 bm_pango_get_layout(struct cairo *cairo, struct
cairo_paint *paint, const
char *
buffer)
81 PangoLayout *layout = pango_cairo_create_layout(cairo->cr);
82 pango_layout_set_text(layout, buffer, -1);
83 PangoFontDescription *desc = pango_font_description_from_string(paint->font);
84 pango_layout_set_font_description(layout, desc);
85 pango_layout_set_single_paragraph_mode(layout, 1);
86 pango_font_description_free(desc);
90 __attribute__((unused)) BM_LOG_ATTR(4, 5) static
bool
91 bm_pango_get_text_extents(struct cairo *cairo, struct
cairo_paint *paint, struct
cairo_result *result, const
char *fmt, ...)
93 assert(cairo && paint && result && fmt);
98 bool ret = bm_vrprintf(&
buffer, &blen, fmt, args);
105 PangoLayout *layout = bm_pango_get_layout(cairo, paint,
buffer);
106 pango_layout_get_pixel_extents(layout, &rect, NULL);
107 g_object_unref(layout);
109 result->x_advance = rect.x + rect.width;
110 result->height = rect.height;
114 __attribute__((unused)) BM_LOG_ATTR(4, 5) static
bool
115 bm_cairo_draw_line(struct cairo *cairo, struct
cairo_paint *paint, struct
cairo_result *result, const
char *fmt, ...)
117 assert(cairo && paint && result && fmt);
122 bool ret = bm_vrprintf(&
buffer, &blen, fmt, args);
128 PangoLayout *layout = bm_pango_get_layout(cairo, paint,
buffer);
129 pango_cairo_update_layout(cairo->cr, layout);
132 pango_layout_get_pixel_size(layout, &width, &height);
133 int base = pango_layout_get_baseline(layout) / PANGO_SCALE;
134 int yoff = height - base;
136 cairo_set_source_rgba(cairo->cr, paint->bg.r, paint->bg.b, paint->bg.g, paint->bg.a);
137 cairo_rectangle(cairo->cr,
138 paint->pos.x - paint->box.lx, paint->pos.y - paint->box.ty,
139 (paint->box.w > 0 ? paint->box.w : width) + paint->box.rx + paint->box.lx,
140 (paint->box.h > 0 ? paint->box.h : height) + paint->box.by + paint->box.ty);
141 cairo_fill(cairo->cr);
143 cairo_set_source_rgba(cairo->cr, paint->fg.r, paint->fg.b, paint->fg.g, paint->fg.a);
144 cairo_move_to(cairo->cr, paint->box.lx + paint->pos.x, paint->pos.y - yoff + paint->box.ty);
145 pango_cairo_show_layout(cairo->cr, layout);
147 g_object_unref(layout);
149 result->x_advance = width + paint->box.rx;
150 result->height = height + paint->box.by;
154 __attribute__((unused)) static
void
155 bm_cairo_color_from_menu_color(const struct bm_menu *menu, enum
bm_color color, struct
cairo_color *c)
158 c->r = (float)menu->colors[color].r / 255.0f;
159 c->g = (
float)menu->colors[color].g / 255.0f;
160 c->b = (float)menu->colors[color].b / 255.0f;
164 __attribute__((unused)) static
void
165 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)
167 assert(cairo && menu && out_result);
170 out_result->displayed = 1;
172 cairo_set_source_rgb(cairo->cr, 0, 0, 0);
173 cairo_rectangle(cairo->cr, 0, 0, width, height);
174 cairo_fill(cairo->cr);
177 memset(&paint, 0,
sizeof(paint));
178 paint.font = menu->font.name;
181 memset(&result, 0,
sizeof(result));
183 uint32_t title_x = 0;
185 bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_FG, &paint.fg);
186 bm_cairo_color_from_menu_color(menu, BM_COLOR_TITLE_BG, &paint.bg);
187 paint.pos = (
struct pos){ result.x_advance, 2 };
188 paint.box = (
struct box){ 4, 8, 2, 2, 0, 0 };
189 bm_cairo_draw_line(cairo, &paint, &result,
"%s", menu->title);
190 title_x = result.x_advance;
193 bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg);
194 bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg);
195 paint.pos = (
struct pos){ (menu->title ? 2 : 0) + result.x_advance, 2 };
196 paint.box = (
struct box){ (menu->title ? 2 : 4), 0, 2, 2, width - paint.pos.x, 0 };
197 bm_cairo_draw_line(cairo, &paint, &result,
"%s", (menu->filter ? menu->filter :
""));
198 const uint32_t titleh = result.height;
199 out_result->height = titleh;
203 uint32_t lines = (menu->lines > 0 ? menu->lines : 1);
205 if (menu->lines > 0) {
208 const bool scrollbar = (menu->scrollbar > BM_SCROLLBAR_NONE && (menu->scrollbar != BM_SCROLLBAR_AUTOHIDE || count > lines) ?
true :
false);
209 const uint32_t spacing_x = (scrollbar ? 4 : 0);
210 uint32_t spacing_y = 0;
211 if (lines > max_height / titleh) {
213 lines = max_height / titleh - 1;
217 uint32_t prefix_x = 0;
219 bm_pango_get_text_extents(cairo, &paint, &result,
"%s ", menu->prefix);
220 prefix_x += result.x_advance;
223 uint32_t posy = titleh;
224 const uint32_t page = (menu->index / lines) * lines;
225 for (uint32_t l = 0, i = page; l < lines && i < count && posy < max_height; ++i, ++l) {
229 bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_FG, &paint.fg);
230 bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_BG, &paint.bg);
231 }
else if (bm_menu_item_is_selected(menu, items[i])) {
232 bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_FG, &paint.fg);
233 bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_BG, &paint.bg);
235 bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_FG, &paint.fg);
236 bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_BG, &paint.bg);
239 if (menu->prefix && highlighted) {
240 paint.pos = (
struct pos){ 0, 2 + posy };
241 paint.box = (
struct box){ 4 + spacing_x, 0, 2, 2, width - paint.pos.x, 0 };
242 bm_cairo_draw_line(cairo, &paint, &result,
"%s %s", menu->prefix, (items[i]->text ? items[i]->text :
""));
244 paint.pos = (
struct pos){ 0, 2 + posy };
245 paint.box = (
struct box){ 4 + spacing_x + prefix_x, 0, 2, 2, width - paint.pos.x, 0 };
246 bm_cairo_draw_line(cairo, &paint, &result,
"%s", (items[i]->text ? items[i]->text :
""));
249 posy += (spacing_y ? spacing_y : result.height);
250 out_result->height = posy + 2;
251 out_result->displayed++;
254 if (scrollbar && count > 0) {
255 bm_cairo_color_from_menu_color(menu, BM_COLOR_SCROLLBAR_BG, &paint.bg);
256 bm_cairo_color_from_menu_color(menu, BM_COLOR_SCROLLBAR_FG, &paint.fg);
258 const uint32_t sheight = out_result->height - titleh;
259 cairo_set_source_rgba(cairo->cr, paint.bg.r, paint.bg.b, paint.bg.g, paint.bg.a);
260 cairo_rectangle(cairo->cr, 0, titleh, 2, sheight);
261 cairo_fill(cairo->cr);
263 const float percent = fmin(((
float)page / (count - lines)), 1.0f);
264 const uint32_t size = fmax(sheight * ((
float)lines / count), 2.0f);
265 const uint32_t posy = percent * (sheight - size);
266 cairo_set_source_rgba(cairo->cr, paint.fg.r, paint.fg.b, paint.fg.g, paint.fg.a);
267 cairo_rectangle(cairo->cr, 0, titleh + posy, 2, size);
268 cairo_fill(cairo->cr);
272 bm_pango_get_text_extents(cairo, &paint, &result,
"lorem ipsum lorem ipsum lorem ipsum lorem");
273 uint32_t cl = fmin(title_x + result.x_advance, width / 4);
275 if (menu->wrap || menu->index > 0) {
276 paint.pos = (
struct pos){ cl, 2 };
277 paint.box = (
struct box){ 1, 2, 2, 2, 0, 0 };
278 bm_cairo_draw_line(cairo, &paint, &result,
"<");
279 cl += result.x_advance + 1;
282 for (uint32_t i = menu->index; i < count && cl < width; ++i) {
286 bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_FG, &paint.fg);
287 bm_cairo_color_from_menu_color(menu, BM_COLOR_HIGHLIGHTED_BG, &paint.bg);
288 }
else if (bm_menu_item_is_selected(menu, items[i])) {
289 bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_FG, &paint.fg);
290 bm_cairo_color_from_menu_color(menu, BM_COLOR_SELECTED_BG, &paint.bg);
292 bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_FG, &paint.fg);
293 bm_cairo_color_from_menu_color(menu, BM_COLOR_ITEM_BG, &paint.bg);
296 paint.pos = (
struct pos){ cl, 2 };
297 paint.box = (
struct box){ 2, 4, 2, 2, 0, 0 };
298 bm_cairo_draw_line(cairo, &paint, &result,
"%s", (items[i]->text ? items[i]->text :
""));
299 cl += result.x_advance + 2;
300 out_result->displayed += (cl < width);
301 out_result->height = fmax(out_result->height, result.height);
304 if (menu->wrap || menu->index + 1 < count) {
305 bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_FG, &paint.fg);
306 bm_cairo_color_from_menu_color(menu, BM_COLOR_FILTER_BG, &paint.bg);
307 bm_pango_get_text_extents(cairo, &paint, &result,
">");
308 paint.pos = (
struct pos){ width - result.x_advance - 2, 2 };
309 paint.box = (
struct box){ 1, 2, 2, 2, 0, 0 };
310 bm_cairo_draw_line(cairo, &paint, &result,
">");