Merged in daid/ugfx (pull request #4)
Add smooth scrolling option to ugfx list.
This commit is contained in:
commit
49beaba572
2 changed files with 123 additions and 40 deletions
|
@ -46,22 +46,30 @@ typedef struct GEventGWinList {
|
|||
typedef struct GListObject {
|
||||
GWidgetObject w;
|
||||
|
||||
#if GINPUT_NEED_MOUSE
|
||||
coord_t start_mouse_x;
|
||||
coord_t start_mouse_y;
|
||||
coord_t last_mouse_y;
|
||||
#endif
|
||||
#if GINPUT_NEED_TOGGLE
|
||||
uint16_t t_up;
|
||||
uint16_t t_dn;
|
||||
#endif
|
||||
|
||||
int cnt; // Number of items currently in the list (quicker than counting each time)
|
||||
int top; // The element at the top of the visible list area
|
||||
int top; // Viewing offset in pixels from the top of the list
|
||||
gfxQueueASync list_head; // The list of items
|
||||
} GListObject;
|
||||
|
||||
/**
|
||||
* @brief Enum to change the behaviour of the scroll bar
|
||||
*
|
||||
* @note This might be used with @p gwinListSetScroll()
|
||||
* @note Used with @p gwinListSetScroll()
|
||||
* @note @p scrollAlways always show the scrollbar
|
||||
* @note @p scrollAuto show the scrollbar when there are more items on the list then fit on the screen
|
||||
* @note @p scrollSmooth enable touch screen smooth scrolling
|
||||
*/
|
||||
typedef enum scroll_t { scrollAlways, scrollAuto } scroll_t;
|
||||
typedef enum scroll_t { scrollAlways, scrollAuto, scrollSmooth } scroll_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -97,7 +105,7 @@ GHandle gwinGListCreate(GDisplay *g, GListObject *widget, GWidgetInit *pInit, bo
|
|||
/**
|
||||
* @brief Change the behaviour of the scroll bar
|
||||
*
|
||||
* @note Current possible values: @p scrollAlways and @p scrollAuto
|
||||
* @note Current possible values: @p scrollAlways, @p scrollAuto and @p scrollSmooth
|
||||
*
|
||||
* @param[in] gh The widget handle (must be a list handle)
|
||||
* @param[in] flag The behaviour to be set
|
||||
|
|
147
src/gwin/list.c
147
src/gwin/list.c
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "gwin/class_gwin.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// user for the default drawing routine
|
||||
#define SCROLLWIDTH 16 // the border from the scroll buttons to the frame
|
||||
|
@ -38,6 +39,7 @@
|
|||
#define GLIST_FLG_MULTISELECT (GWIN_FIRST_CONTROL_FLAG << 0)
|
||||
#define GLIST_FLG_HASIMAGES (GWIN_FIRST_CONTROL_FLAG << 1)
|
||||
#define GLIST_FLG_SCROLLALWAYS (GWIN_FIRST_CONTROL_FLAG << 2)
|
||||
#define GLIST_FLG_SCROLLSMOOTH (GWIN_FIRST_CONTROL_FLAG << 3)
|
||||
|
||||
// Flags on a ListItem.
|
||||
#define GLIST_FLG_SELECTED 0x0001
|
||||
|
@ -94,7 +96,17 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|||
x = 1;
|
||||
|
||||
// the scroll area
|
||||
if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) {
|
||||
if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH) {
|
||||
iwidth = gw->g.width - 2 - 4;
|
||||
if (gw2obj->cnt > 0) {
|
||||
int max_scroll_value = gw2obj->cnt * iheight - gw->g.height-2;
|
||||
if (max_scroll_value > 0) {
|
||||
int bar_height = (gw->g.height-2) * (gw->g.height-2) / (gw2obj->cnt * iheight);
|
||||
gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + 1, 2, gw->g.height-1, gw->pstyle->background);
|
||||
gdispGFillArea(gw->g.display, gw->g.x + gw->g.width-4, gw->g.y + gw2obj->top * ((gw->g.height-2)-bar_height) / max_scroll_value, 2, bar_height, ps->edge);
|
||||
}
|
||||
}
|
||||
} else if ((gw2obj->cnt > (gw->g.height-2) / iheight) || (gw->g.flags & GLIST_FLG_SCROLLALWAYS)) {
|
||||
iwidth = gw->g.width - (SCROLLWIDTH+3);
|
||||
gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128));
|
||||
gdispGDrawLine(gw->g.display, gw->g.x+iwidth+1, gw->g.y+1, gw->g.x+iwidth+1, gw->g.y+gw->g.height-2, ps->edge);
|
||||
|
@ -118,10 +130,16 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|||
|
||||
|
||||
// Find the top item
|
||||
for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i++);
|
||||
for (qi = gfxQueueASyncPeek(&gw2obj->list_head), i = iheight - 1; i < gw2obj->top && qi; qi = gfxQueueASyncNext(qi), i+=iheight);
|
||||
|
||||
// the list frame
|
||||
gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
|
||||
|
||||
// Set the clipping region so we do not override the frame.
|
||||
gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
|
||||
|
||||
// Draw until we run out of room or items
|
||||
for (y=1; y+iheight < gw->g.height-1 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
|
||||
for (y = 1-(gw2obj->top%iheight); y < gw->g.height-2 && qi; qi = gfxQueueASyncNext(qi), y += iheight) {
|
||||
fill = (qi2li->flags & GLIST_FLG_SELECTED) ? ps->fill : gw->pstyle->background;
|
||||
#if GWIN_NEED_LIST_IMAGES
|
||||
if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
|
||||
|
@ -146,12 +164,41 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|||
// Fill any remaining item space
|
||||
if (y < gw->g.height-1)
|
||||
gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, gw->g.height-1-y, gw->pstyle->background);
|
||||
|
||||
// the list frame
|
||||
gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, ps->edge);
|
||||
}
|
||||
|
||||
#if GINPUT_NEED_MOUSE
|
||||
static void MouseSelect(GWidgetObject* gw, coord_t x, coord_t y) {
|
||||
const gfxQueueASyncItem* qi;
|
||||
int item, i;
|
||||
coord_t iheight;
|
||||
|
||||
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + TEXTGAP;
|
||||
|
||||
// Handle click over the list area
|
||||
item = (gw2obj->top + y) / iheight;
|
||||
|
||||
if (item < 0 || item >= gw2obj->cnt)
|
||||
return;
|
||||
|
||||
for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
||||
if ((gw->g.flags & GLIST_FLG_MULTISELECT)) {
|
||||
if (item == i) {
|
||||
qi2li->flags ^= GLIST_FLG_SELECTED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (item == i)
|
||||
qi2li->flags |= GLIST_FLG_SELECTED;
|
||||
else
|
||||
qi2li->flags &=~ GLIST_FLG_SELECTED;
|
||||
}
|
||||
}
|
||||
|
||||
_gwidgetRedraw(&gw->g);
|
||||
sendListEvent(gw, item);
|
||||
|
||||
}
|
||||
|
||||
// a mouse down has occurred over the list area
|
||||
static void MouseDown(GWidgetObject* gw, coord_t x, coord_t y) {
|
||||
const gfxQueueASyncItem* qi;
|
||||
|
@ -159,20 +206,32 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|||
coord_t iheight;
|
||||
(void) x;
|
||||
|
||||
gw2obj->start_mouse_x = x;
|
||||
gw2obj->start_mouse_y = y;
|
||||
gw2obj->last_mouse_y = y;
|
||||
|
||||
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + TEXTGAP;
|
||||
pgsz = (gw->g.height-2)/iheight;
|
||||
pgsz = (gw->g.height-2);
|
||||
if (pgsz < 1) pgsz = 1;
|
||||
|
||||
// For smooth scrolling, scrolling is done in the MouseMove and selection is done on MouseUp
|
||||
if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
|
||||
return;
|
||||
|
||||
// Handle click over the scroll bar
|
||||
if (gw2obj->cnt > pgsz && x >= gw->g.width-(SCROLLWIDTH+2)) {
|
||||
if (gw2obj->cnt > (pgsz / iheight) && x >= gw->g.width-(SCROLLWIDTH+2)) {
|
||||
if (y < 2*ARROW) {
|
||||
if (gw2obj->top > 0) {
|
||||
gw2obj->top--;
|
||||
gw2obj->top -= iheight;
|
||||
if (gw2obj->top < 0)
|
||||
gw2obj->top = 0;
|
||||
_gwidgetRedraw(&gw->g);
|
||||
}
|
||||
} else if (y >= gw->g.height - 2*ARROW) {
|
||||
if (gw2obj->top < gw2obj->cnt - pgsz) {
|
||||
gw2obj->top++;
|
||||
if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
|
||||
gw2obj->top += iheight;
|
||||
if (gw2obj->top > gw2obj->cnt * iheight - pgsz)
|
||||
gw2obj->top = gw2obj->cnt * iheight - pgsz;
|
||||
_gwidgetRedraw(&gw->g);
|
||||
}
|
||||
} else if (y < gw->g.height/2) {
|
||||
|
@ -184,39 +243,51 @@ static void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
|
|||
_gwidgetRedraw(&gw->g);
|
||||
}
|
||||
} else {
|
||||
if (gw2obj->top < gw2obj->cnt - pgsz) {
|
||||
if (gw2obj->top < gw2obj->cnt - 2*pgsz)
|
||||
if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
|
||||
if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz)
|
||||
gw2obj->top += pgsz;
|
||||
else
|
||||
gw2obj->top = gw2obj->cnt - pgsz;
|
||||
gw2obj->top = gw2obj->cnt * iheight - pgsz;
|
||||
_gwidgetRedraw(&gw->g);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle click over the list area
|
||||
item = gw2obj->top + y / iheight;
|
||||
MouseSelect(gw, x, y);
|
||||
}
|
||||
|
||||
if (item < 0 || item >= gw2obj->cnt)
|
||||
return;
|
||||
static void MouseUp(GWidgetObject* gw, coord_t x, coord_t y) {
|
||||
// Only act when we are a smooth scrolling list
|
||||
if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH))
|
||||
return;
|
||||
|
||||
for(qi = gfxQueueASyncPeek(&gw2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
|
||||
if ((gw->g.flags & GLIST_FLG_MULTISELECT)) {
|
||||
if (item == i) {
|
||||
qi2li->flags ^= GLIST_FLG_SELECTED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (item == i)
|
||||
qi2li->flags |= GLIST_FLG_SELECTED;
|
||||
else
|
||||
qi2li->flags &=~ GLIST_FLG_SELECTED;
|
||||
}
|
||||
}
|
||||
// Only allow selection when we did not scroll
|
||||
if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4)
|
||||
return;
|
||||
|
||||
_gwidgetRedraw(&gw->g);
|
||||
sendListEvent(gw, item);
|
||||
MouseSelect(gw, x, y);
|
||||
}
|
||||
|
||||
static void MouseMove(GWidgetObject* gw, coord_t x, coord_t y) {
|
||||
int iheight, oldtop;
|
||||
(void) x;
|
||||
|
||||
if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH)) return;
|
||||
|
||||
if (gw2obj->last_mouse_y != y) {
|
||||
oldtop = gw2obj->top;
|
||||
iheight = gdispGetFontMetric(gw->g.font, fontHeight) + TEXTGAP;
|
||||
|
||||
gw2obj->top -= y - gw2obj->last_mouse_y;
|
||||
if (gw2obj->top >= gw2obj->cnt * iheight - (gw->g.height-2))
|
||||
gw2obj->top = gw2obj->cnt * iheight - (gw->g.height-2) - 1;
|
||||
if (gw2obj->top < 0)
|
||||
gw2obj->top = 0;
|
||||
gw2obj->last_mouse_y = y;
|
||||
if (oldtop != gw2obj->top)
|
||||
_gwidgetRedraw(&gw->g);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -295,8 +366,8 @@ static const gwidgetVMT listVMT = {
|
|||
#if GINPUT_NEED_MOUSE
|
||||
{
|
||||
MouseDown,
|
||||
0,
|
||||
0,
|
||||
MouseUp,
|
||||
MouseMove,
|
||||
},
|
||||
#endif
|
||||
#if GINPUT_NEED_TOGGLE
|
||||
|
@ -340,14 +411,18 @@ void gwinListSetScroll(GHandle gh, scroll_t flag) {
|
|||
if (gh->vmt != (gwinVMT *)&listVMT)
|
||||
return;
|
||||
|
||||
((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS);
|
||||
switch (flag) {
|
||||
case scrollAlways:
|
||||
((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
|
||||
break;
|
||||
|
||||
case scrollAuto:
|
||||
((GListObject*)gh)->w.g.flags &=~ GLIST_FLG_SCROLLALWAYS;
|
||||
break;
|
||||
|
||||
case scrollSmooth:
|
||||
((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue