#include "cltr-list.h" #include "cltr-private.h" #define ANIM_FPS 50 #define FPS_TO_TIMEOUT(t) (1000/(t)) struct CltrListCell { CltrRect rect; Pixbuf *thumb_pixb; CltrTexture *thumb_texture; Pixbuf *text_pixb; CltrTexture *text_texture; }; struct CltrList { CltrWidget widget; GList *cells, *active_cell; int active_cell_y; int cell_height; int cell_width; int n_cells; CltrListCellActivate cell_activate_cb; gpointer cell_activate_data; CltrListState state; int scroll_dir; }; static void cltr_list_show(CltrWidget *widget); static gboolean cltr_list_handle_xevent (CltrWidget *widget, XEvent *xev); static void cltr_list_paint(CltrWidget *widget); static float distfunc(CltrList *list, int d) { int maxdist = list->widget.height; d = (maxdist-ABS(d)) ; return ( exp( (float)d/maxdist * 0.8 ) / exp(0.8) ) ; } CltrListCell* cltr_list_cell_new(CltrList *list, Pixbuf *thumb_pixb, char *text) { CltrListCell *cell = NULL; ClutterFont *font; PixbufPixel pixel = { 0, 0, 0, 0 }, font_pixel = { 255, 255, 255, 255}; font = font_new ("Sans Bold 32"); cell = g_malloc0(sizeof(CltrListCell)); cell->thumb_pixb = thumb_pixb; pixbuf_ref(cell->thumb_pixb); cell->thumb_texture = cltr_texture_new(cell->thumb_pixb); cell->text_pixb = pixbuf_new(list->cell_width - (list->cell_width/4), list->cell_height); pixbuf_fill_rect(cell->text_pixb, 0, 0, -1, -1, &pixel); font_draw(font, cell->text_pixb, text, 0, 0, &font_pixel); cell->text_texture = cltr_texture_new(cell->text_pixb); return cell; } void cltr_list_cell_set_pixbuf(CltrListCell *cell, Pixbuf *thumb_pixb) { cltr_texture_unref(cell->thumb_texture); cell->thumb_pixb = thumb_pixb; cell->thumb_texture = cltr_texture_new(cell->thumb_pixb); } CltrWidget* cltr_list_new(int width, int height, int cell_width, int cell_height) { CltrList *list; list = g_malloc0(sizeof(CltrList)); list->widget.width = width; list->widget.height = height; list->widget.show = cltr_list_show; list->widget.paint = cltr_list_paint; list->cell_height = cell_height; /* maximum */ list->cell_width = cell_width; /* maximum */ list->widget.xevent_handler = cltr_list_handle_xevent; return CLTR_WIDGET(list); } void cltr_list_append_cell(CltrList *list, CltrListCell *cell) { list->cells = g_list_append(list->cells, cell); if (!list->active_cell) list->active_cell = g_list_first(list->cells); list->n_cells++; } /* * This is messy hack cos, cells arn't real widgets :( * */ gboolean cltr_list_get_active_cell_co_ords(CltrList *list, int *x1, int *y1, int *x2, int *y2) { if (list->active_cell) { CltrListCell *cell = list->active_cell->data; *x1 = cltr_rect_x1(cell->rect); *y1 = cltr_rect_y1(cell->rect); *x2 = cltr_rect_x2(cell->rect); *y2 = cltr_rect_y2(cell->rect); return TRUE; } return FALSE; } static void cltr_list_show(CltrWidget *widget) { CltrList *list = CLTR_LIST(widget); CltrListCell *cell = NULL; if (list->active_cell_y == 0) { list->active_cell_y = (widget->height / 2) - (list->cell_height/2); list->active_cell = g_list_first(list->cells); cell = list->active_cell->data; cell->rect.y = list->active_cell_y; } list->state = CLTR_LIST_STATE_BROWSE; cltr_list_update_layout(list); cltr_widget_queue_paint(widget); } void cltr_list_on_activate_cell(CltrList *list, CltrListCellActivate callback, gpointer *userdata) { list->cell_activate_cb = callback; list->cell_activate_data = userdata; } CltrListCell* cltr_list_get_active_cell(CltrList *list) { if (list->active_cell) return list->active_cell->data; return NULL; } static gboolean cltr_list_handle_xevent (CltrWidget *widget, XEvent *xev) { CltrList *list = CLTR_LIST(widget); switch (xev->type) { case KeyPress: { KeySym kc; kc = XKeycodeToKeysym(xev->xkey.display, xev->xkey.keycode, 0); switch (kc) { case XK_Up: case XK_KP_Up: cltr_list_scroll_up(list); break; case XK_Down: case XK_KP_Down: cltr_list_scroll_down(list); break; case XK_Return: if (list->cell_activate_cb && list->active_cell) list->cell_activate_cb(list, list->active_cell->data, list->cell_activate_data); break; case XK_Left: case XK_KP_Left: case XK_Right: case XK_KP_Right: default: CLTR_DBG("unhandled keysym"); } } break; } return TRUE; } static void cltr_list_animate(CltrList *list) { GList *cell_item = NULL; CltrListCell *next_active = NULL, *cell_top = NULL; cell_top = (CltrListCell *)g_list_nth_data(list->cells, 0); int i = 0; for (;;) { if (list->state == CLTR_LIST_STATE_SCROLL_UP) { cell_item = g_list_previous(list->active_cell); if (!cell_item) { list->state = CLTR_LIST_STATE_BROWSE; return; } next_active = (CltrListCell *)cell_item->data; if (next_active->rect.y < list->active_cell_y) { cell_top->rect.y += 1; } else { list->active_cell = cell_item; list->state = CLTR_LIST_STATE_BROWSE; return; } } else if (list->state == CLTR_LIST_STATE_SCROLL_DOWN) { cell_item = g_list_next(list->active_cell); if (!cell_item) { list->state = CLTR_LIST_STATE_BROWSE; return; } next_active = (CltrListCell *)cell_item->data; if (next_active->rect.y > list->active_cell_y) { cell_top->rect.y -= 1; } else { list->active_cell = cell_item; list->state = CLTR_LIST_STATE_BROWSE; return; } } if (++i > 10) return; cltr_list_update_layout(list); } } gboolean cltr_list_timeout_cb(gpointer data) { CltrList *list = (CltrList *)data; cltr_list_animate(list); cltr_widget_queue_paint(CLTR_WIDGET(list)); switch(list->state) { case CLTR_LIST_STATE_SCROLL_UP: case CLTR_LIST_STATE_SCROLL_DOWN: return TRUE; case CLTR_LIST_STATE_LOADING: case CLTR_LIST_STATE_LOAD_COMPLETE: case CLTR_LIST_STATE_BROWSE: default: return FALSE; } } static void cltr_list_update_layout(CltrList *list) { GList *cell_item = NULL; CltrListCell *cell = NULL; int last; cell_item = g_list_first(list->cells); cell = (CltrListCell *)cell_item->data; last = cell->rect.y; while (cell_item) { float scale = 0.0; cell = (CltrListCell *)cell_item->data; cell->rect.y = last; if (cell->rect.y + cell->rect.height >= 0) { scale = distfunc(list, cell->rect.y - list->active_cell_y); cell->rect.width = list->cell_width * scale; cell->rect.height = list->cell_height * scale; cell->rect.x = (list->widget.width - cell->rect.width) / 2; } last = cell->rect.y + cell->rect.height; cell_item = g_list_next(cell_item); } } static void cltr_list_paint(CltrWidget *widget) { GList *cell_item = NULL; CltrList *list = CLTR_LIST(widget); CltrListCell *cell = NULL; PixbufPixel col = { 0xff, 0, 0, 0xff }; int last; CLTR_MARK(); cell_item = g_list_first(list->cells); cell = (CltrListCell *)cell_item->data; last = cell->rect.y; glPushMatrix(); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); cltr_list_update_layout(list); while (cell_item) { float scale = 0.0; col.r = 0xff; col.g = 0; col.b = 0; col.a = 0xff; cell = (CltrListCell *)cell_item->data; last = cell->rect.y + cell->rect.height; scale = distfunc(list, cell->rect.y - list->active_cell_y); if (last > 0 && cell->rect.y < list->widget.width) /* crappy clip */ { glDisable(GL_TEXTURE_2D); if (cell_item == list->active_cell && list->state == CLTR_LIST_STATE_BROWSE) col.b = 0xff; else col.b = 0x00; cltr_glu_rounded_rect_filled(cltr_rect_x1(cell->rect), cltr_rect_y1(cell->rect) + (5.0 * scale), cltr_rect_x2(cell->rect), cltr_rect_y2(cell->rect) - (5.0 * scale), 10, &col); col.r = 0xff; col.g = 0xff; col.b = 0xff; col.a = 0xff; /* cltr_glu_rounded_rect(cltr_rect_x1(cell->rect) + 10, cltr_rect_y1(cell->rect) + 12, cltr_rect_x2(cell->rect) - 10, cltr_rect_y2(cell->rect) - 12, 10, &col); */ glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(1.0, 1.0, 1.0, 1.0); cltr_texture_render_to_gl_quad(cell->text_texture, cltr_rect_x1(cell->rect) + 100, cltr_rect_y1(cell->rect) + 10, cltr_rect_x2(cell->rect) - 100, cltr_rect_y2(cell->rect) - 10); cltr_texture_render_to_gl_quad(cell->thumb_texture, cltr_rect_x1(cell->rect), cltr_rect_y1(cell->rect), cltr_rect_x1(cell->rect) + 80 , cltr_rect_y1(cell->rect) + 60); glDisable(GL_BLEND); } cell_item = g_list_next(cell_item); } glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glPopMatrix(); } void cltr_list_scroll_down(CltrList *list) { list->state = CLTR_LIST_STATE_SCROLL_DOWN; g_timeout_add(FPS_TO_TIMEOUT(ANIM_FPS), cltr_list_timeout_cb, list); } void cltr_list_scroll_up(CltrList *list) { list->state = CLTR_LIST_STATE_SCROLL_UP; g_timeout_add(FPS_TO_TIMEOUT(ANIM_FPS), cltr_list_timeout_cb, list); }