Files
multirom_m86/lib/tabview.c
2015-02-16 21:01:06 +01:00

289 lines
7.0 KiB
C

/*
* This file is part of MultiROM.
*
* MultiROM 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 3 of the License, or
* (at your option) any later version.
*
* MultiROM 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 MultiROM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "tabview.h"
#include "containers.h"
#include "util.h"
#include "animation.h"
#include "log.h"
#include "input.h"
struct tabview_page
{
fb_item_pos **items;
int last_offset;
};
typedef struct tabview_page tabview_page;
static void tabview_page_destroy(tabview_page *p)
{
list_clear(&p->items, NULL);
free(p);
}
static void tabview_page_update_offset(tabview_page *p, int offset)
{
if(!p->items || offset == p->last_offset)
return;
fb_item_pos **itr;
const int diff = offset - p->last_offset;
for(itr = p->items; *itr; ++itr)
(*itr)->x += diff;
p->last_offset = offset;
}
int tabview_touch_handler(touch_event *ev, void *data)
{
tabview *t = data;
if(t->touch_id == -1 && (ev->changed & TCHNG_ADDED))
{
if (ev->x < t->x || ev->y < t->y ||
ev->x > t->x+t->w || ev->y > t->y+t->h)
{
return -1;
}
t->touch_id = ev->id;
t->touch_moving = 0;
touch_tracker_start(t->tracker, ev);
if(t->anim_id != ANIM_INVALID_ID)
{
anim_cancel(t->anim_id, 0);
t->anim_id = ANIM_INVALID_ID;
}
return -1;
}
if(t->touch_id != ev->id)
return -1;
if(ev->changed & TCHNG_REMOVED)
{
t->touch_id = -1;
touch_tracker_finish(t->tracker, ev);
if(!t->touch_moving)
return -1;
if(t->pos % t->w != 0)
{
int page_idx, duration = 100;
float page = ((float)t->pos)/t->w;
if(page < 0)
page_idx = 0;
else if(page >= t->count - 1)
page_idx = t->count - 1;
else
{
float velocity = touch_tracker_get_velocity(t->tracker, TRACKER_X);
if(fabs(velocity) >= 1000.f)
{
page_idx = (int)page;
if(velocity < 0.f)
++page_idx;
duration = iabs(t->pos - page_idx*t->w)/(fabs(velocity*DPI_MUL)/1000);
}
else
page_idx = (int)(page + 0.5f);
}
if(page_idx != t->curr_page)
{
if(t->on_page_changed_by_swipe)
t->on_page_changed_by_swipe(page_idx);
t->curr_page = page_idx;
}
tabview_set_active_page(t, page_idx, duration);
}
return -1;
}
if(ev->changed & TCHNG_POS)
{
touch_tracker_add(t->tracker, ev);
if(!t->touch_moving)
{
if (t->tracker->distance_abs_x >= 25*DPI_MUL && t->tracker->distance_abs_x > t->tracker->distance_abs_y*3)
{
t->touch_moving = 1;
ev->changed |= TCHNG_REMOVED;
ev->x = -1;
ev->y = -1;
t->pos += -t->tracker->distance_x;
tabview_update_positions(t);
}
return -1;
}
t->pos += t->tracker->prev_x - ev->x;
tabview_update_positions(t);
return 1;
}
return -1;
}
tabview *tabview_create(int x, int y, int w, int h)
{
tabview *t = mzalloc(sizeof(tabview));
t->x = x;
t->y = y;
t->w = w;
t->h = h;
t->anim_id = ANIM_INVALID_ID;
t->touch_id = -1;
t->tracker = touch_tracker_create();
pthread_mutex_init(&t->mutex, NULL);
return t;
}
void tabview_destroy(tabview *t)
{
rm_touch_handler(&tabview_touch_handler, t);
pthread_mutex_destroy(&t->mutex);
list_clear(&t->pages, tabview_page_destroy);
touch_tracker_destroy(t->tracker);
free(t);
}
void tabview_add_page(tabview *t, int idx)
{
if(idx == -1)
idx = t->count;
tabview_page *p = mzalloc(sizeof(tabview_page));
pthread_mutex_lock(&t->mutex);
list_add_at(&t->pages, idx, p);
++t->count;
t->fullW = t->count*t->w;
pthread_mutex_unlock(&t->mutex);
}
void tabview_rm_page(tabview *t, int idx)
{
if(idx < 0 || idx >= t->count)
return;
pthread_mutex_lock(&t->mutex);
list_rm_at(&t->pages, idx, tabview_page_destroy);
--t->count;
t->fullW = t->count*t->w;
pthread_mutex_unlock(&t->mutex);
}
void tabview_add_item(tabview *t, int page_idx, void *fb_item)
{
if(page_idx < 0 || page_idx >= t->count)
return;
pthread_mutex_lock(&t->mutex);
list_add(&t->pages[page_idx]->items, fb_item);
pthread_mutex_unlock(&t->mutex);
}
void tabview_add_items(tabview *t, int page_idx, void *fb_items)
{
if(page_idx < 0 || page_idx >= t->count || !fb_items)
return;
pthread_mutex_lock(&t->mutex);
list_add_from_list(&t->pages[page_idx]->items, fb_items);
pthread_mutex_unlock(&t->mutex);
}
void tabview_rm_item(tabview *t, int page_idx, void *fb_item)
{
if(page_idx < 0 || page_idx >= t->count)
return;
pthread_mutex_lock(&t->mutex);
list_rm(&t->pages[page_idx]->items, fb_item, NULL);
pthread_mutex_unlock(&t->mutex);
}
void tabview_update_positions(tabview *t)
{
int i;
int x = 0;
if(t->last_reported_pos != t->pos)
{
if(t->on_pos_changed)
t->on_pos_changed(((float)t->pos)/t->w);
t->last_reported_pos = t->pos;
}
fb_batch_start();
pthread_mutex_lock(&t->mutex);
for(i = 0; i < t->count; ++i)
{
tabview_page_update_offset(t->pages[i], x - t->pos);
x += t->w;
}
pthread_mutex_unlock(&t->mutex);
fb_batch_end();
fb_request_draw();
}
static void tabview_move_anim_step(void *data, float interpolated)
{
tabview *t = data;
t->pos = t->anim_pos_start + (t->anim_pos_diff * interpolated);
tabview_update_positions(t);
}
void tabview_set_active_page(tabview *t, int page_idx, int anim_duration)
{
if(page_idx < 0 || page_idx >= t->count)
return;
if(t->curr_page == page_idx && t->pos == page_idx*t->w)
return;
if(t->anim_id != ANIM_INVALID_ID)
anim_cancel(t->anim_id, 0);
t->curr_page = page_idx;
if(anim_duration == 0)
{
t->pos = page_idx*t->w;
tabview_update_positions(t);
return;
}
t->anim_pos_start = t->pos;
t->anim_pos_diff = page_idx*t->w - t->pos;
call_anim *a = call_anim_create(t, tabview_move_anim_step, anim_duration, INTERPOLATOR_DECELERATE);
t->anim_id = a->id;
call_anim_add(a);
}