aboutsummaryrefslogtreecommitdiff
path: root/src/state.c
blob: d0e2e12e84298bee6f6304245a128dd0316d6970 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <glib.h>

#include "state.h"
#include "log.h"

State *state_create(Window *win, Drawer *draw)
{
    State *state = g_malloc0(sizeof(State));
    g_assert_nonnull(state);
    state->win = win;
    state->draw = draw;
    return state;
}

void state_add_button(State *state, Button *btn)
{
    state->btns = g_list_append(state->btns, btn);
}

void state_remove_button(State *state, Button *btn)
{
    state->btns = g_list_remove(state->btns, btn);
}

static gint align_compare(gconstpointer a, gconstpointer b)
{
    PangoAlignment a_align = CAST(a, Button)->align;
    PangoAlignment b_align = CAST(b, Button)->align;

    if (a_align < b_align) return -1;
    if (a_align > b_align) return 1;
    return 0;
}

void state_order_button(State *state)
{
    state->btns = g_list_sort(state->btns, align_compare);
}

static gboolean redraw_handler(State *state)
{
    log_debug("Redrawing once [relayout=%d]", state->relayout);

    if (state->relayout) {
        draw_compute_layout(state->draw, state->win, state->btns);
        state->relayout = false;
    }

    draw_paint(state->draw, state->win);

    state->idle_id = 0;
    return G_SOURCE_REMOVE;
}

void state_request_redraw(State *state, bool relayout)
{
    // Override old redraw
    if (state->idle_id != 0)
        g_source_remove(state->idle_id);

    state->relayout = relayout;

    if (state->anim_id != 0) return;

    state->idle_id = g_idle_add(G_SOURCE_FUNC(redraw_handler), state);
}

static bool anim_check_more(State *state, GList *btns)
{
    bool more = false;
    for (GList *it = btns; it; it = it->next) {
        Button *btn = it->data;

        if (btn->anim != NULL) {
            bool draw = btn->anim->paint_func != NULL;
            bool layout = btn->anim->layout_func != NULL;

            // Remove animation if both paint_func and layout_func finished
            if (!draw && !layout) {
                log_debug("Removing animation [type=%d, end=%ld, button=%p]",
                    btn->anim->type, btn->anim->start + btn->anim->duration, btn);

                animation_destroy(btn->anim);
                btn->anim = NULL;
            } else {
                more = true;
                state->relayout |= layout;
            }
        }

        if (!btn->simple && anim_check_more(state, CAST(btn, ButtonGroup)->children))
            more = true;
    }
    return more;
}

static gboolean anim_handler(State *state)
{
    bool more = anim_check_more(state, state->btns);
    log_debug("Redrawing animation [relayout=%d, more=%d]", state->relayout, more);

    if (state->relayout) {
        draw_compute_layout(state->draw, state->win, state->btns);
        state->relayout = false;
    }

    draw_paint(state->draw, state->win);

    if (!more) state->anim_id = 0;
    return more;
}

void state_request_animation(State *state)
{
    if (state->idle_id != 0) {
        g_source_remove(state->idle_id);
        state->idle_id = 0;
    }

    if (state->anim_id != 0) return;
    if (!anim_handler(state)) return;

    const int fps = 60;
    state->anim_id = g_timeout_add(1000 / fps, G_SOURCE_FUNC(anim_handler), state);
}

void state_destroy(State *state)
{
    g_list_free_full(state->btns, (void *)button_destroy);
    g_free(state);
}

// vim: ts=4 sw=4 et