aboutsummaryrefslogtreecommitdiff
path: root/src/draw.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/draw.c')
-rw-r--r--src/draw.c391
1 files changed, 0 insertions, 391 deletions
diff --git a/src/draw.c b/src/draw.c
deleted file mode 100644
index 44baba6..0000000
--- a/src/draw.c
+++ /dev/null
@@ -1,391 +0,0 @@
-#include <glib.h>
-#include <math.h>
-#include <pango/pangocairo.h>
-#include <pango/pango-font.h>
-#include <pango/pango-types.h>
-
-#include "draw.h"
-#include "button.h"
-#include "log.h"
-
-#define ANIMATION(btn, which, ...) \
- do { \
- if ((btn)->anim != NULL && (btn)->anim->which##_func != NULL) { \
- if ((btn)->anim->start == 0) { \
- (btn)->anim->start = g_get_monotonic_time(); \
- log_debug("Starting animation [type=%d, func=%s, start=%ld, duration=%ld, button=%p]", \
- (btn)->anim->type, #which, (btn)->anim->start, (btn)->anim->duration, (btn)); \
- } \
- if (!(btn)->anim->which##_func((btn)->anim, __VA_ARGS__)) \
- (btn)->anim->which##_func = NULL; \
- } \
- } while (false)
-
-Drawer *draw_create()
-{
- Drawer *draw = g_malloc0(sizeof(Drawer));
- g_assert_nonnull(draw);
- return draw;
-}
-
-static void paint_button(cairo_t *cr, const Layout *layout)
-{
- double degree = M_PI / 180.0;
- int radius = (layout->height - 2 * layout->y_pad) / 2;
- int line_radius = radius - layout->line_w / 2;
-
-#if 0
- // Debug lines
-
- // Layout size
- cairo_set_source_rgb(cr, 0, 0, 0);
- cairo_move_to(cr, layout->x, layout->y);
- cairo_line_to(cr, layout->x, layout->y + layout->height);
- cairo_stroke(cr);
-
- cairo_move_to(cr, layout->x + layout->width, layout->y);
- cairo_line_to(cr, layout->x + layout->width, layout->y + layout->height);
- cairo_stroke(cr);
-
- // Layout padding
- cairo_set_source_rgb(cr, 0.5, 0.1, 0.1);
- cairo_rectangle(cr, layout->x + layout->x_pad, layout->y + layout->y_pad, layout->width - 2 * layout->x_pad, layout->height - 2 * layout->y_pad);
- cairo_stroke(cr);
-#endif
-
- cairo_set_line_width(cr, layout->line_w);
-
- // Button background
- Color color = layout->btn->color;
- cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- cairo_new_sub_path(cr);
- cairo_arc(cr, layout->x + layout->x_pad + radius, layout->y + layout->y_pad + radius, radius, 90 * degree, 270 * degree);
- cairo_arc(cr, layout->x + layout->width - layout->x_pad - radius, layout->y + layout->y_pad + radius, radius, 270 * degree, 450 * degree);
- cairo_close_path(cr);
- cairo_fill(cr);
-
- // Button border
- color = layout->btn->line_color;
- cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- cairo_new_sub_path(cr);
- cairo_arc(cr, layout->x + layout->x_pad + radius, layout->y + layout->y_pad + radius, line_radius, 90 * degree, 270 * degree);
- cairo_arc(cr, layout->x + layout->width - layout->x_pad - radius, layout->y + layout->y_pad + radius, line_radius, 270 * degree, 450 * degree);
- cairo_close_path(cr);
- cairo_stroke(cr);
-}
-
-static void paint_text(cairo_t *cr, const Layout *layout)
-{
- int text_x = layout->x + layout->width / 2 - layout->text_w / 2;
- int text_y = layout->y + layout->height / 2 - layout->text_h / 2;
-
- Color color = CAST(layout->btn, ButtonSimple)->text_color;
- cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- cairo_move_to(cr, text_x, text_y);
-
- // NOTE: This works only if the text didn't change in size after the layouting
- pango_layout_set_text(layout->pl, CAST(layout->btn, ButtonSimple)->text, -1);
- pango_cairo_update_layout(cr, layout->pl);
- pango_cairo_show_layout(cr, layout->pl);
-}
-
-static void paint_button_list(cairo_t *cr, GList *layouts)
-{
- for (GList *it = layouts; it; it = it->next) {
- Layout *layout = it->data;
- Button *btn = layout->btn;
-
- cairo_push_group(cr);
-
- // Apply before_func on the layout and cairo context
- ANIMATION(btn, before, layout, cr);
-
- paint_button(cr, layout);
-
- if (btn->simple) {
- g_assert_null(layout->children);
- paint_text(cr, layout);
- } else {
- g_assert_nonnull(layout->children);
- paint_button_list(cr, layout->children);
- }
-
- // Apply after_func on the layout and cairo context
- ANIMATION(btn, after, layout, cr);
-
- cairo_pop_group_to_source(cr);
- cairo_paint(cr);
- }
-}
-
-void draw_paint(Drawer *draw, Window *win)
-{
- // 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);
- cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
-
- // Fill the background
- cairo_set_source_rgba(cr, draw->background.r, draw->background.g, draw->background.b, draw->background.a);
- cairo_paint(cr);
-
- // Treat the top-level buttons similarly to a button group
- paint_button_list(cr, draw->layouts);
- cairo_destroy(cr);
-
- // Use device pixels
- double scale = window_get_scale(win);
- window_move(win, draw->left_pad * scale, draw->top_pad * scale);
- window_resize(win, draw->width * scale, draw->height * scale);
-
- window_paint_surface(win, surface, draw->width * scale, draw->height * scale);
- cairo_surface_destroy(surface);
-}
-
-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_invalidate(GList *layouts)
-{
- g_list_free_full(layouts, (GDestroyNotify)layout_destroy);
-}
-
-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);
- const char *text = CAST(layout->btn, ButtonSimple)->text;
-
- pango_layout_set_font_description(layout->pl, desc);
- pango_layout_set_alignment(layout->pl, PANGO_ALIGN_CENTER);
- pango_layout_set_height(layout->pl, 0);
- pango_layout_set_text(layout->pl, text, -1);
- 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 + height;
-}
-
-static void compute_width(Drawer *draw, Window *win)
-{
- int screen_width = win->con->screen_size->width;
- double scale = window_get_scale(win);
- draw->width = (screen_width - draw->right_pad - draw->left_pad) / scale;
- log_debug("Draw context width calculated [scale=%lf, width=%d]", scale, draw->width);
-}
-
-static void layout_check_width(Layout *layout)
-{
- // FIXME: Max width is not implemented correctly
- if (layout->btn->max_width > 0)
- layout->width = MIN(layout->btn->max_width, layout->width);
-
- if (layout->btn->min_width > 0)
- layout->width = MAX(layout->btn->min_width, layout->width);
-}
-
-static GList *compute_group_layout(Drawer *draw, GList *btns, int bx, bool root)
-{
- GList *layouts = NULL;
- for (GList *it = btns; it; it = it->next) {
- Button *btn = it->data;
- PangoAlignment align = btn->align;
-
- Layout *layout = g_malloc0(sizeof(Layout));
- layouts = g_list_prepend(layouts, layout);
-
- layout->btn = btn;
- layout->height = draw->height;
- layout->line_w = btn->line_width;
- layout->x_pad = btn->x_pad;
- layout->y_pad = btn->y_pad;
- layout->x = bx;
- layout->y = 0;
-
-retry:
- if (!btn->simple) {
- ButtonGroup *group = CAST(btn, ButtonGroup);
- g_assert_nonnull(group->children);
-
- // NOTE: If a group has only one children treat it as a single button
- // As a consequence btn->align can't be trusted
- if (group->children->next == NULL) {
- // XXX: We should also keep track of the group animation
- // Is this the right way to do it? Can it loop?
- ANIMATION(btn, layout, layout);
-
- layout->btn = btn = group->children->data;
- goto retry;
- } else {
- layout->children = compute_group_layout(draw, group->children, bx, false);
- g_assert_nonnull(layout->children);
-
- Layout *last = g_list_last(layout->children)->data;
- layout->width = last->x + last->width - bx;
-
- // FIXME: Temporary solution to make the antialiasing (or the circles not
- // overlapping correctly) problem less noticeable
- bool fix_left = CAST(layout->children->data, Layout)->x_pad == 0;
- if (fix_left) {
- layout->x += 1;
- layout->width -= 1;
- }
-
- bool fix_right = last->x_pad == 0;
- if (fix_right)
- layout->width -= 1;
- }
- } else {
- layout->pl = pango_layout_new(draw->context);
- layout_set_text(layout, draw->desc, draw->height);
- }
-
- // NOTE: We add half a line width on both sides
- layout->width += layout->line_w;
- layout_check_width(layout);
-
- // Apply layout_func on the layout
- ANIMATION(btn, layout, layout);
-
- bx += layout->width;
- if (root) {
- draw->layout_bx[align] = bx;
- draw->layout_end[align] = layouts;
- }
-
- if (it->next != NULL && (!root || CAST(it->next->data, Button)->align == align))
- bx += draw->sep;
- }
-
- // Otherwise button_action_find will not work!
- layouts = g_list_reverse(layouts);
- return layouts;
-}
-
-void draw_compute_layout(Drawer *draw, Window *win, GList *btns)
-{
- // Move this somewhere else
- compute_width(draw, win);
-
- double scale = window_get_scale(win);
- cairo_surface_set_device_scale(win->surface, scale, scale);
- pango_cairo_context_set_resolution(draw->context, scale * 96);
- pango_cairo_update_context(win->cr, draw->context);
-
- memset(draw->layout_end, 0, sizeof(draw->layout_end));
- memset(draw->layout_bx, 0, sizeof(draw->layout_bx));
-
- g_clear_pointer(&draw->layouts, layout_invalidate);
- draw->layouts = compute_group_layout(draw, btns, 0, true);
-
- 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 (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);
- }
-
- bool has_left = draw->layout_end[PANGO_ALIGN_LEFT] != NULL;
- bool has_center = draw->layout_end[PANGO_ALIGN_CENTER] != NULL;
-
- GList *center_start = has_left ? draw->layout_end[PANGO_ALIGN_LEFT]->next : draw->layouts;
- GList *right_start = has_center ? draw->layout_end[PANGO_ALIGN_CENTER]->next :
- has_left ? draw->layout_end[PANGO_ALIGN_LEFT]->next : draw->layouts;
-
- if (center_start != NULL) {
- 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);
- }
-
- if (right_start != NULL) {
- 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]);
-
- log_debug("Aligning right layout [x=%d, off=%d]", right, right_off);
- layout_adjust(right_start, NULL, right_off);
- }
-
- log_debug("Updated layouts");
-}
-
-void draw_compute_text_size(Drawer *draw, const char *text, int *text_w, int *text_h)
-{
- PangoLayout *pl = pango_layout_new(draw->context);
- pango_layout_set_font_description(pl, draw->desc);
- pango_layout_set_height(pl, 0);
- pango_layout_set_alignment(pl, PANGO_ALIGN_CENTER);
- pango_layout_set_text(pl, text, -1);
- pango_layout_get_pixel_size(pl, text_w, text_h);
- g_object_unref(pl);
-}
-
-void draw_set_background(Drawer *draw, Color background)
-{
- draw->background = background;
-}
-
-void draw_set_separator(Drawer *draw, int sep)
-{
- g_assert(sep >= 0);
- draw->sep = sep;
-}
-
-void draw_set_font(Drawer *draw, const char *font)
-{
- log_debug("Pango loading font description '%s'", font);
- draw->desc = pango_font_description_from_string(font);
- g_assert_nonnull(draw->desc);
- log_debug("Pango found matching font '%s'", pango_font_description_get_family(draw->desc));
-}
-
-// NOTE: Don't call this between layout and paint!
-void draw_set_context(Drawer *draw, PangoContext *context)
-{
- g_assert_nonnull(context);
- draw->context = context;
- log_debug("Pango context updated [context=%p]", context);
-}
-
-void draw_set_size(Drawer *draw, int height, int left_pad, int right_pad, int top_pad)
-{
- g_assert(height > 0);
-
- draw->height = height;
- draw->left_pad = left_pad;
- draw->right_pad = right_pad;
- draw->top_pad = top_pad;
-
- log_debug("Draw context size updated [height=%d, left_pad=%d, right_pad=%d, top_pad=%d]",
- height, left_pad, right_pad, top_pad);
-}
-
-void draw_destroy(Drawer *draw)
-{
- g_clear_pointer(&draw->layouts, layout_invalidate);
- pango_font_description_free(draw->desc);
- g_free(draw);
-}
-
-// vim: ts=4 sw=4 et