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
134
135
136
|
#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 layout = btn->anim->layout_func != NULL;
bool before = btn->anim->before_func != NULL;
bool after = btn->anim->after_func != NULL;
// Remove animation if all functions finished
if (!layout && !before && !after) {
log_debug("Removing animation [type=%d, end=%ld, button=%p]",
btn->anim->type, btn->anim->start + btn->anim->duration, btn);
g_clear_pointer(&btn->anim, animation_destroy);
} 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;
}
// FIXME: When spamming animations there seem to be occasional
// race condition that block animations from playing
// (the animation timeout eagerly quits)
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, (GDestroyNotify)button_destroy);
g_free(state);
}
// vim: ts=4 sw=4 et
|