#include #include #include #include #include "log.h" #include "layout.h" #include "block.h" void layout_init(layout_t *layout, block_t *block, layout_info_t info) { memset(layout, 0, sizeof(layout_t)); assert(!block->hidden); layout->block = block; layout->x = info.x_offset; layout->y = 0; layout->x_padding = block->x_padding; layout->y_padding = block->y_padding; layout->height = info.height; layout->line_width = block->line_width; switch (block->type) { case BLOCK_TEXT: { block_text_t *text = (block_text_t *)block; layout->pl = pango_layout_new(info.context); pango_layout_set_font_description(layout->pl, info.fontdesc); pango_layout_set_alignment(layout->pl, text->text_align); pango_layout_set_height(layout->pl, 0); pango_layout_set_text(layout->pl, text->text, -1); pango_layout_get_pixel_size(layout->pl, &layout->text_width, &layout->text_height); int length = pango_layout_get_character_count(layout->pl); layout->width = info.height + (length != 1) * layout->text_width; break; } case BLOCK_GROUP: { block_group_t *group = (block_group_t *)block; int x_offset = info.x_offset; layout->children = calloc(group->n_children, sizeof(layout_t)); for (size_t i = 0; i < group->n_children; i++) { if (group->children[i]->hidden) continue; layout_init(&layout->children[layout->n_children], group->children[i], info); info.x_offset += layout->children[layout->n_children].width + group->spacing; layout->n_children++; } if (group->collapse && layout->n_children == 1) { layout_t *children = layout->children; memcpy(layout, children, sizeof(layout_t)); free(children); } else if (layout->n_children > 0) { layout_t *last = &layout->children[layout->n_children - 1]; layout->width = last->x + last->width - x_offset; // NOTE: Temporary solution to make blocks not overlapping correctly // less noticeable if (layout->children[0].x_padding == 0) { layout->x += 1; layout->width -= 1; } if (last->x_padding == 0) layout->width -= 1; } break; } case BLOCK_DYNAMIC: { // You are on your own block_dynamic_t *spec = (block_dynamic_t *)block; spec->layout_fn(block, layout, info); // TODO: Add more checks assert(layout->width > 0); assert(layout->height > 0); break; } default: unreachable(); } if (layout->block->max_width > 0 && layout->width > layout->block->max_width) layout->width = layout->block->max_width; if (layout->block->min_width > 0 && layout->width < layout->block->min_width) layout->width = layout->block->min_width; } void layout_render(layout_t *layout, cairo_t *cr) { const int radius = layout->height / 2 - layout->y_padding; int block_x = layout->x + layout->x_padding; int block_y = layout->y + layout->y_padding; // Update gradients cairo_matrix_t matrix; cairo_matrix_init_scale(&matrix, 1.0 / layout->width, 1.0); cairo_matrix_translate(&matrix, -layout->x, 0.0); // Render background cairo_pattern_t *pattern = layout->block->bg_color.pattern; if (pattern != NULL) { render_capsule_fast(cr, block_x, block_y, layout->width - layout->x_padding, radius, radius); cairo_pattern_set_matrix(pattern, &matrix); cairo_set_source(cr, pattern); cairo_fill(cr); } // Render border pattern = layout->block->line_color.pattern; if (pattern != NULL) { int line_radius = radius; render_capsule_fast(cr, block_x, block_y, layout->width - layout->x_padding, radius, line_radius); cairo_pattern_set_matrix(pattern, &matrix); cairo_set_source(cr, pattern); cairo_set_line_width(cr, layout->block->line_width); cairo_stroke(cr); } switch (layout->block->type) { case BLOCK_TEXT: { block_text_t *text = (block_text_t *)layout->block; assert(layout->n_children == 0); int text_x = layout->x + (layout->width - layout->text_width + layout->x_padding) / 2; int text_y = layout->y + (layout->height - layout->text_height) / 2; color_t color = text->text_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); cairo_move_to(cr, text_x, text_y); pango_cairo_update_layout(cr, layout->pl); pango_cairo_show_layout(cr, layout->pl); break; } case BLOCK_GROUP: { for (size_t i = 0; i < layout->n_children; i++) { cairo_push_group(cr); layout_render(&layout->children[i], cr); cairo_pop_group_to_source(cr); cairo_paint(cr); } break; } case BLOCK_DYNAMIC: { // NOTE: What if a special block adds children to the layout? // For now do nothing and let them handle it, however // this is a strange behavior in my opinion... // block_dynamic_t *spec = (block_dynamic_t *)layout->block; spec->render_fn(layout, cr); break; } default: unreachable(); } } void layout_free(layout_t *layout) { if (layout->pl != NULL) g_object_unref(layout->pl); for (size_t i = 0; i < layout->n_children; i++) layout_free(&layout->children[i]); free(layout->children); }