diff options
Diffstat (limited to 'src/draw.c')
| -rw-r--r-- | src/draw.c | 239 |
1 files changed, 107 insertions, 132 deletions
@@ -8,16 +8,9 @@ #include "button.h" #include "log.h" -static void layout_destroy(Layout *layout) -{ - if (layout->pl != NULL) g_object_unref(layout->pl); - g_list_free_full(layout->children, (GDestroyNotify)layout_destroy); - g_free(layout); -} - Drawer *draw_create(const char *font, int height, int left_pad, int right_pad, int top_pad) { - Drawer *draw = g_malloc(sizeof(Drawer)); + Drawer *draw = g_malloc0(sizeof(Drawer)); g_assert_nonnull(draw); log_debug("Pango loading font description '%s'", font); @@ -29,8 +22,6 @@ Drawer *draw_create(const char *font, int height, int left_pad, int right_pad, i draw->right_pad = right_pad; draw->top_pad = top_pad; - draw->layouts = NULL; - log_debug("Draw context created [height=%d, left_pad=%d, right_pad=%d, top_pad=%d]", height, left_pad, right_pad, top_pad); @@ -38,13 +29,10 @@ Drawer *draw_create(const char *font, int height, int left_pad, int right_pad, i } // TODO: Remove this -static void compute_width(Drawer *draw, Window *win, int *width, int *height) +static void compute_width(Drawer *draw, Window *win) { int screen_width = win->con->screen_size->width; - int screen_height = win->con->screen_size->height; - - *width = round(screen_width - draw->right_pad - draw->left_pad); - *height = round(draw->height); + draw->width = round(screen_width - draw->right_pad - draw->left_pad); } static void paint_button(cairo_t *cr, const Layout *layout) @@ -139,10 +127,10 @@ static void paint_button_list(cairo_t *cr, GList *layouts) void draw_paint(Drawer *draw, Window *win) { - int width, height; - compute_width(draw, win, &width, &height); + compute_width(draw, win); - cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + // Back buffering + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, draw->width, draw->height); cairo_t *cr = cairo_create(surface); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); @@ -157,13 +145,30 @@ void draw_paint(Drawer *draw, Window *win) cairo_destroy(cr); window_move(win, draw->left_pad, draw->top_pad); - window_resize(win, width, draw->height); + window_resize(win, draw->width, draw->height); - window_paint_surface(win, surface, width, height); + window_paint_surface(win, surface, draw->width, draw->height); cairo_surface_destroy(surface); } -static void layout_set_text(Layout *layout, PangoFontDescription *desc, int height, int radius) +static void layout_destroy(Layout *layout) +{ + if (layout->pl != NULL) g_object_unref(layout->pl); + g_list_free_full(layout->children, (GDestroyNotify)layout_destroy); + g_free(layout); +} + +static void layout_adjust(GList *start, GList *end, int off) +{ + // NOTE: end must be present in the list or segfault + for (GList *it = start; it != end; it = it->next) { + Layout *layout = it->data; + layout->x += off; + layout_adjust(layout->children, NULL, off); + } +} + +static void layout_set_text(Layout *layout, PangoFontDescription *desc, int height) { g_assert(layout->btn->simple); @@ -172,167 +177,132 @@ static void layout_set_text(Layout *layout, PangoFontDescription *desc, int heig pango_layout_set_font_description(layout->pl, desc); pango_layout_set_text(layout->pl, text, -1); pango_layout_set_alignment(layout->pl, PANGO_ALIGN_CENTER); - - int text_w, text_h; - pango_layout_get_pixel_size(layout->pl, &text_w, &text_h); - - layout->text_w = ceil(text_w); - layout->text_h = ceil(text_h); + pango_layout_get_pixel_size(layout->pl, &layout->text_w, &layout->text_h); // If there is only one glyph the button should be round size_t text_l = g_utf8_strlen(text, -1); - layout->width = text_l == 1 ? height : layout->text_w + 2 * radius; + layout->width = text_l == 1 ? height : layout->text_w + height; layout->height = height; } -void draw_compute_layout(Drawer *draw, Window *win, GList *btns) +static GList *compute_group_layout(Drawer *draw, Window *win, GList *btns, int bx, bool root) { - g_list_free_full(draw->layouts, (GDestroyNotify)layout_destroy); - draw->layouts = NULL; - - int width, height; - compute_width(draw, win, &width, &height); - - Button *prev = NULL; - - int x = 0; - int radius = height / 2; - int sep = 10; - - GList *layout_start[3] = { NULL }; - int layout_end[3] = { 0 }; - + GList *layouts = NULL; for (GList *it = btns; it; it = it->next) { Button *btn = it->data; - Layout *layout = g_malloc(sizeof(Layout)); - layout->btn = btn; - layout->children = NULL; - - if (prev != NULL && prev->align == btn->align) x += sep; + Layout *layout = g_malloc0(sizeof(Layout)); + layouts = g_list_prepend(layouts, layout); + layout->btn = btn; layout->line_w = btn->line_width; layout->x_pad = btn->x_pad; layout->y_pad = btn->y_pad; - - layout->x = x; + layout->x = bx; layout->y = 0; - draw->layouts = g_list_prepend(draw->layouts, layout); - - if (prev == NULL || prev->align != btn->align) { - layout_start[btn->align] = draw->layouts; - } - + // TODO: Allow nested groups if (!btn->simple) { ButtonGroup *group = CAST(btn, ButtonGroup); g_assert_nonnull(group->children); // If there is only one child treat a group like a single button if (group->children->next == NULL) { - Button *child = group->children->data; - g_assert_true(child->simple); - layout->btn = child; + layout->btn = btn = group->children->data; + g_assert_true(btn->simple); } else { - for (GList *it = group->children; it; it = it->next) { - Button *btn = it->data; - // Nested groups are not allowed - g_assert_true(btn->simple); - - Layout *child = g_malloc(sizeof(Layout)); - child->btn = btn; - child->children = NULL; - child->line_w = btn->line_width; - - child->x_pad = btn->x_pad; - child->y_pad = btn->y_pad; - - child->x = x; - child->y = 0; - - child->pl = pango_cairo_create_layout(window_get_context(win)); - layout_set_text(child, draw->desc, height, radius); - child->width += child->line_w; - - layout->children = g_list_prepend(layout->children, child); - - x += child->width; - if (it->next != NULL) x += sep; - } - - layout->pl = NULL; - layout->width = x - layout->x; - layout->height = height; + layout->children = compute_group_layout(draw, win, group->children, bx, false); + g_assert_nonnull(layout->children); - // Otherwise button_action_find will not work! - layout->children = g_list_reverse(layout->children); + Layout *last = g_list_last(layout->children)->data; + layout->width = last->x + last->width - bx; + layout->height = draw->height; // FIXME: Temporary solution to make the antialiasing (or the circles not overlapping correctly) problem less noticeable - - bool fix_left = CAST(g_list_first(group->children)->data, Button)->x_pad == 0; - bool fix_right = CAST(g_list_last(group->children)->data, Button)->x_pad == 0; - + bool fix_left = CAST(layout->children->data, Layout)->x_pad == 0; if (fix_left) { layout->x += 1; layout->width -= 1; } - if (fix_right) layout->width -= 1; + + bool fix_right = last->x_pad == 0; + if (fix_right) + layout->width -= 1; } } - if (layout->btn->simple) { + // NOTE: This check is only apparently redundant because it handles the case where there + // there is only a single children in a group + if (btn->simple) { layout->pl = pango_cairo_create_layout(window_get_context(win)); - layout_set_text(layout, draw->desc, height, radius); - - // TODO: make it work for groups - if (btn->anim != NULL && btn->anim->layout_func != NULL) { - if (btn->anim->start == 0) { - btn->anim->start = g_get_monotonic_time(); - log_debug("Starting animation [type=%d, start=%ld, duration=%ld, button=%p]", - btn->anim->type, btn->anim->start, btn->anim->duration, btn); - } + layout_set_text(layout, draw->desc, draw->height); + } - if (!btn->anim->layout_func(btn->anim, layout)) - btn->anim->layout_func = NULL; + // NOTE: We add half a line width on both sides + layout->width += layout->line_w; + + if (btn->anim != NULL && btn->anim->layout_func != NULL) { + if (btn->anim->start == 0) { + btn->anim->start = g_get_monotonic_time(); + log_debug("Starting animation [type=%d, start=%ld, duration=%ld, button=%p]", + btn->anim->type, btn->anim->start, btn->anim->duration, btn); } - layout->width += layout->line_w; - x += layout->width; + if (!btn->anim->layout_func(btn->anim, layout)) + btn->anim->layout_func = NULL; + } + + bx += layout->width; + if (root) { + draw->layout_bx[btn->align] = bx; + draw->layout_end[btn->align] = layouts; } - prev = btn; - layout_end[btn->align] = x; + if (it->next != NULL && (!root || CAST(it->next->data, Button)->align == btn->align)) + bx += draw->sep; } - draw->layouts = g_list_reverse(draw->layouts); + // Otherwise button_action_find will not work! + layouts = g_list_reverse(layouts); + return layouts; +} + +void draw_compute_layout(Drawer *draw, Window *win, GList *btns) +{ + compute_width(draw, win); + + g_list_free_full(draw->layouts, (GDestroyNotify)layout_destroy); + draw->layouts = compute_group_layout(draw, win, btns, 0, true); - int layout_width[3] = { - layout_end[PANGO_ALIGN_LEFT], - layout_end[PANGO_ALIGN_CENTER] - layout_end[PANGO_ALIGN_LEFT], - layout_end[PANGO_ALIGN_RIGHT] - MAX(layout_end[PANGO_ALIGN_CENTER], layout_end[PANGO_ALIGN_LEFT]), - }; + draw->layout_width[PANGO_ALIGN_LEFT] = draw->layout_bx[PANGO_ALIGN_LEFT]; + draw->layout_width[PANGO_ALIGN_CENTER] = draw->layout_bx[PANGO_ALIGN_CENTER] - draw->layout_bx[PANGO_ALIGN_LEFT]; + draw->layout_width[PANGO_ALIGN_RIGHT] = draw->layout_bx[PANGO_ALIGN_RIGHT] - MAX(draw->layout_bx[PANGO_ALIGN_CENTER], draw->layout_bx[PANGO_ALIGN_LEFT]); - if (layout_end[PANGO_ALIGN_RIGHT] > width) { - log_error("Layout is bigger than the window (%d vs %d)", layout_end[PANGO_ALIGN_RIGHT], width); + if (draw->layout_bx[PANGO_ALIGN_RIGHT] > draw->width) { + log_error("Layout is bigger than the window (%d vs %d)", draw->layout_bx[PANGO_ALIGN_RIGHT], draw->width); } - int center = round(width / 2); - int center_off = center - round(layout_width[PANGO_ALIGN_CENTER] / 2) - layout_end[PANGO_ALIGN_LEFT]; - log_debug("Aligning center layout [center=%d, off=%d]", center, center_off); + bool has_center = draw->layout_end[PANGO_ALIGN_LEFT] && draw->layout_end[PANGO_ALIGN_LEFT]->next; + bool has_right = draw->layout_end[PANGO_ALIGN_LEFT] && draw->layout_end[PANGO_ALIGN_CENTER]->next; - for (GList *it = layout_start[PANGO_ALIGN_CENTER]; it != NULL && it != layout_start[PANGO_ALIGN_RIGHT]; it = it->next) { - Layout *layout = it->data; - layout->x += center_off; + GList *center_start = has_center ? draw->layout_end[PANGO_ALIGN_LEFT]->next : NULL; + GList *right_start = has_right ? draw->layout_end[PANGO_ALIGN_CENTER]->next : NULL; + + if (has_center) { + int center = round(draw->width / 2); + int center_off = center - draw->layout_width[PANGO_ALIGN_CENTER] / 2 - draw->layout_bx[PANGO_ALIGN_LEFT]; + + log_debug("Aligning center layout [center=%d, off=%d]", center, center_off); + layout_adjust(center_start, right_start, center_off); } - int right = width - layout_width[PANGO_ALIGN_RIGHT]; - int right_off = right - MAX(layout_end[PANGO_ALIGN_LEFT], layout_end[PANGO_ALIGN_CENTER]); - log_debug("Aligning right layout [x=%d, off=%d]", right, right_off); + if (has_right) { + int right = draw->width - draw->layout_width[PANGO_ALIGN_RIGHT]; + int right_off = right - MAX(draw->layout_bx[PANGO_ALIGN_LEFT], draw->layout_bx[PANGO_ALIGN_CENTER]); - for (GList *it = layout_start[PANGO_ALIGN_RIGHT]; it != NULL; it = it->next) { - Layout *layout = it->data; - layout->x += right_off; + log_debug("Aligning right layout [x=%d, off=%d]", right, right_off); + layout_adjust(right_start, NULL, right_off); } log_debug("Updated layouts"); @@ -343,6 +313,11 @@ void draw_set_background(Drawer *draw, Color background) draw->background = background; } +void draw_set_separator(Drawer *draw, int sep) +{ + draw->sep = sep; +} + void draw_destroy(Drawer *draw) { g_list_free_full(draw->layouts, (GDestroyNotify)layout_destroy); |
