diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2009-10-23 19:47:29 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2009-10-23 19:47:29 +0200 |
commit | 57c3e22114062dfdfe00944e93bc07476d45137f (patch) | |
tree | 906f0ab20cb30521096165c391e0f9699c361165 | |
parent | cb169a518b82993cd50ba11cb91a7aea4ef0dae2 (diff) |
animation effects WIP
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | gtkplaylist.c | 118 | ||||
-rw-r--r-- | gtkplaylist.h | 3 | ||||
-rw-r--r-- | timeline.c | 122 | ||||
-rw-r--r-- | timeline.h | 52 |
5 files changed, 291 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am index 0808d526..7bf75fb8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,7 +45,8 @@ deadbeef_SOURCES =\ session.h session.c gtksession.c\ junklib.h junklib.c utf8.c utf8.h\ optmath.h\ - vfs.c vfs.h vfs_stdio.c + vfs.c vfs.h vfs_stdio.c\ + timeline.c timeline.h sdkdir = $(pkgincludedir) sdk_HEADERS = deadbeef.h diff --git a/gtkplaylist.c b/gtkplaylist.c index 5dcd6792..a4fe67c7 100644 --- a/gtkplaylist.c +++ b/gtkplaylist.c @@ -45,6 +45,7 @@ #include "session.h" #include "deadbeef.h" #include "conf.h" +#include "timeline.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -97,6 +98,78 @@ static int header_dragging = -1; static int header_sizing = -1; static int header_dragpt[2]; +#define COLHDR_ANIM_TIME 0.2f + +typedef struct { + int c1; + int c2; + int x1, x2; + int dx1, dx2; + // animated values + int ax1, ax2; + timeline_t *timeline; + int anim_active; + gtkplaylist_t *pl; +} colhdr_animator_t; + +static colhdr_animator_t colhdr_anim; + +static gboolean +redraw_header (colhdr_animator_t *anim) { + gtkpl_header_draw (anim->pl); + gtkpl_expose_header (anim->pl, 0, 0, anim->pl->header->allocation.width, anim->pl->header->allocation.height); + return FALSE; +} + + +static int +colhdr_anim_cb (float _progress, int _last, void *_ctx) { + colhdr_animator_t *anim = (colhdr_animator_t *)_ctx; + anim->ax1 = anim->x1 + (float)(anim->dx1 - anim->x1) * _progress; + anim->ax2 = anim->x2 + (float)(anim->dx2 - anim->x2) * _progress; +// printf ("%f %d %d\n", _progress, anim->ax1, anim->ax2); + g_idle_add (redraw_header, anim); + if (_last) { + anim->anim_active = 0; + } + return 0; +} + +static void +colhdr_anim_swap (gtkplaylist_t *pl, int c1, int c2, int x1, int x2) { + // interrupt previous anim + if (!colhdr_anim.timeline) { + colhdr_anim.timeline = timeline_create (); + } + colhdr_anim.pl = pl; + + colhdr_anim.c1 = c1; + colhdr_anim.c2 = c2; + + // find c1 and c2 in column list and setup coords + // note: columns are already swapped, so their coords must be reversed, + // as if before swap + gtkpl_column_t *c; + int idx = 0; + int x = 0; + for (c = pl->columns; c; c = c->next, idx++) { + if (idx == c1) { + colhdr_anim.x1 = x1; + colhdr_anim.dx2 = x; + } + else if (idx == c2) { + colhdr_anim.x2 = x2; + colhdr_anim.dx1 = x; + } + x += c->width; + } + colhdr_anim.anim_active = 1; + timeline_stop (colhdr_anim.timeline, 0); + timeline_init (colhdr_anim.timeline, COLHDR_ANIM_TIME, 100, colhdr_anim_cb, &colhdr_anim); + printf ("timeline_start\n"); + timeline_start (colhdr_anim.timeline); +} + // that must be called before gtk_init void gtkpl_init (void) { @@ -110,6 +183,10 @@ gtkpl_init (void) { void gtkpl_free (gtkplaylist_t *pl) { + if (colhdr_anim.timeline) { + timeline_free (colhdr_anim.timeline, 1); + colhdr_anim.timeline = 0; + } while (pl->columns) { gtkpl_column_t *next = pl->columns->next; gtkpl_column_free (pl->columns); @@ -1176,16 +1253,25 @@ gtkpl_header_draw (gtkplaylist_t *ps) { int idx = 0; for (c = ps->columns; c; c = c->next, idx++) { w = c->width; + int xx = x; + if (colhdr_anim.anim_active) { + if (idx == colhdr_anim.c2) { + xx = colhdr_anim.ax1; + } + else if (idx == colhdr_anim.c1) { + xx = colhdr_anim.ax2; + } + } if (header_dragging < 0 || idx != header_dragging) { - if (x >= widget->allocation.width) { + if (xx >= widget->allocation.width) { continue; } if (w > 0) { - gtk_paint_vline (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, NULL, NULL, NULL, 0, h, x+w - 2); + gtk_paint_vline (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, NULL, NULL, NULL, 0, h, xx+w - 2); GdkColor *gdkfg = &widget->style->fg[0]; float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff}; draw_set_fg_color (fg); - draw_text (x + 5, h/2-draw_get_font_size()/2, c->width-10, 0, c->title); + draw_text (xx + 5, h/2-draw_get_font_size()/2, c->width-10, 0, c->title); } } else { @@ -1199,6 +1285,14 @@ gtkpl_header_draw (gtkplaylist_t *ps) { for (c = ps->columns; c; c = c->next, idx++) { w = c->width; if (idx == header_dragging) { + if (colhdr_anim.anim_active) { + if (idx == colhdr_anim.c2) { + x = colhdr_anim.ax1; + } + else if (idx == colhdr_anim.c1) { + x = colhdr_anim.ax2; + } + } // draw empty slot if (x < widget->allocation.width) { gtk_paint_box (widget->style, ps->backbuf_header, GTK_STATE_ACTIVE, GTK_SHADOW_ETCHED_IN, NULL, NULL, "button", x, 0, w, h); @@ -1214,6 +1308,7 @@ gtkpl_header_draw (gtkplaylist_t *ps) { draw_set_fg_color (fg); draw_text (x + 5, h/2-draw_get_font_size()/2, c->width-10, 0, c->title); } + break; } x += w; } @@ -1281,13 +1376,20 @@ on_header_motion_notify_event (GtkWidget *widget, gtkpl_column_t *cc; int x = 0; int idx = 0; + int x1 = -1, x2 = -1; for (cc = ps->columns; cc; cc = cc->next, idx++) { if (x < c->movepos && x + c->width > c->movepos) { inspos = idx; + x1 = x; + } + else if (idx == header_dragging) { + x2 = x; } x += cc->width; } if (inspos >= 0 && inspos != header_dragging) { + int c1 = inspos; + int c2 = header_dragging; // remove c from list if (c == ps->columns) { ps->columns = c->next; @@ -1318,14 +1420,18 @@ on_header_motion_notify_event (GtkWidget *widget, } } } +// colhdr_anim_swap (ps, c1, c2, x1, x2); // force redraw of everything - gtkpl_setup_hscrollbar (ps); +// gtkpl_setup_hscrollbar (ps); gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height); gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height); gtkpl_column_update_config (ps, c, i); } - gtkpl_header_draw (ps); - gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height); + else { + // only redraw that if not animating + gtkpl_header_draw (ps); + gtkpl_expose_header (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height); + } } else if (header_sizing >= 0) { // limit event rate diff --git a/gtkplaylist.h b/gtkplaylist.h index 9cc4bc74..d3656c29 100644 --- a/gtkplaylist.h +++ b/gtkplaylist.h @@ -244,4 +244,7 @@ gtkpl_column_update_config (gtkplaylist_t *pl, gtkpl_column_t *c, int idx); void gtkpl_column_rewrite_config (gtkplaylist_t *pl); +void +gtkpl_expose_header (gtkplaylist_t *ps, int x, int y, int w, int h); + #endif // __GTKPLAYLIST_H diff --git a/timeline.c b/timeline.c new file mode 100644 index 00000000..c9a40fb5 --- /dev/null +++ b/timeline.c @@ -0,0 +1,122 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/time.h> +#include <assert.h> +#include "timeline.h" +#include "threading.h" + +timeline_t * +timeline_create (void) { + timeline_t *tl = malloc (sizeof (timeline_t)); + memset (tl, 0, sizeof (timeline_t)); + return tl; +} + +void +timeline_free (timeline_t *tl, int wait) { + if (tl->tid && wait) { + int tid = tl->tid; + tl->destroy = 1; + thread_join (tid); + } + else { + tl->destroy = 1; + } +} + +void +timeline_init (timeline_t *tl, float seconds, float fps, int (*callback)(float _progress, int _last, void *_ctx), void *ctx) { + tl->fps = fps; + tl->duration = seconds; + tl->progress = 0; + tl->callback = callback; + tl->callback_ctx = ctx; + tl->destroy = 0; + tl->stop = 0; +} + +void +timeline_stop (timeline_t *tl, int wait) { + int tid = tl->tid; + if (tid) { + tl->stop = 1; + if (wait) { + thread_join (tid); + } + } +} + +void +timeline_thread_func (uintptr_t ctx) { + printf ("timeline thread started\n"); + timeline_t *tl = (timeline_t *)ctx; + + for (;;) { + if (tl->stop || tl->destroy) { + tl->callback (1, 1, tl->callback_ctx); + break; + } + struct timeval tm; + gettimeofday (&tm, NULL); + float dt = (tm.tv_sec - tl->time.tv_sec) + (tm.tv_usec - tl->time.tv_usec) / 1000000.0; + float t = tl->progress; + tl->progress += dt; + memcpy (&tl->time, &tm, sizeof (tm)); + if (t > tl->duration) { + tl->callback (1, 1, tl->callback_ctx); + break; + } + else { + if (tl->callback (t/tl->duration, 0, tl->callback_ctx) < 0) { + break; + } + } + printf ("progress: %f\n", tl->progress); + + // sleep until next frame + usleep (1000000 / tl->fps); + } + tl->tid = 0; + if (tl->destroy) { + printf ("timeline %p destroyed\n", tl); + free (tl); + } + printf ("timeline %p thread terminated\n", tl); +} + + +void +timeline_start (timeline_t *tl) { + gettimeofday (&tl->time, NULL); + tl->progress = 0; + tl->stop = 0; + tl->destroy = 0; + if (!tl->tid) { + tl->tid = thread_start (timeline_thread_func, (uintptr_t)tl); + } + else { + printf ("reusing existing thread\n"); + } +} + diff --git a/timeline.h b/timeline.h new file mode 100644 index 00000000..68c372a0 --- /dev/null +++ b/timeline.h @@ -0,0 +1,52 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __TIMELINE_H +#define __TIMELINE_H + +#include <stdint.h> + +typedef struct { + float fps; + float duration; + float progress; + struct timeval time; + intptr_t tid; + int stop; + int destroy; + int (*callback)(float _progress, int _last, void *_ctx); + void *callback_ctx; +} timeline_t; + +// callback must return 0 to continue, or -1 to abort +timeline_t * +timeline_create (void); + +void +timeline_free (timeline_t *timeline, int wait); + +void +timeline_stop (timeline_t *tl, int wait); + +void +timeline_init (timeline_t *timeline, float seconds, float fps, int (*callback)(float _progress, int _last, void *_ctx), void *ctx); + +void +timeline_start (timeline_t *timeline); + +#endif // __TIMELINE_H |