ugfx/src/gwin/gwin_list.c

814 lines
21 KiB
C
Raw Normal View History

2013-07-17 15:49:21 +00:00
/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
2018-10-01 15:32:39 +00:00
* http://ugfx.io/license.html
2013-07-17 15:49:21 +00:00
*/
/**
* @file src/gwin/gwin_list.c
* @brief GWIN list widget header file
2013-07-17 15:49:21 +00:00
*/
#include "../../gfx.h"
2013-07-17 15:49:21 +00:00
#if GFX_USE_GWIN && GWIN_NEED_LIST
#include "gwin_class.h"
2013-07-25 17:15:51 +00:00
#include <string.h>
#include <stdlib.h>
2013-07-25 17:15:51 +00:00
2013-07-28 05:00:08 +00:00
// user for the default drawing routine
#define LST_SCROLLWIDTH 16 // the border from the scroll buttons to the frame
#define LST_ARROW_SZ 10 // arrow side length
#define LST_HORIZ_PAD 5 // extra horizontal padding for text
#define LST_VERT_PAD 2 // extra vertical padding for text
2013-07-27 20:55:32 +00:00
// Macro's to assist in data type conversions
#define gh2obj ((GListObject *)gh)
#define gw2obj ((GListObject *)gw)
#define qi2li ((ListItem *)qi)
#define qix2li ((ListItem *)qix)
#define ple ((GEventGWinList *)pe)
2013-07-25 17:15:51 +00:00
static void sendListEvent(GWidgetObject *gw, int item) {
2013-07-25 17:15:51 +00:00
GSourceListener* psl;
GEvent* pe;
// Trigger a GWIN list event
psl = 0;
2013-07-27 13:23:52 +00:00
2013-07-25 17:15:51 +00:00
while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
if (!(pe = geventGetEventBuffer(psl)))
continue;
2013-07-25 17:15:51 +00:00
ple->type = GEVENT_GWIN_LIST;
ple->gwin = (GHandle)gw;
ple->item = item;
#if GWIN_WIDGET_TAGS
ple->tag = gw->tag;
#endif
2013-07-25 17:15:51 +00:00
geventSendEvent(psl);
}
}
2013-07-17 15:49:21 +00:00
2013-07-25 17:15:51 +00:00
#if GINPUT_NEED_MOUSE
2018-07-08 00:54:19 +00:00
static void ListMouseSelect(GWidgetObject* gw, gCoord x, gCoord y) {
const gfxQueueASyncItem* qi;
int item, i;
2018-07-08 00:54:19 +00:00
gCoord iheight;
(void) x;
iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
// 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;
}
}
_gwinUpdate(&gw->g);
sendListEvent(gw, item);
}
// a mouse down has occurred over the list area
2018-07-08 00:54:19 +00:00
static void ListMouseDown(GWidgetObject* gw, gCoord x, gCoord y) {
gCoord iheight, pgsz;
2014-05-01 03:53:46 +00:00
// Save our mouse start position
gw2obj->start_mouse_x = x;
gw2obj->start_mouse_y = y;
gw2obj->last_mouse_y = y;
// For smooth scrolling, scrolling is done in the ListMouseMove and selection is done on ListMouseUp
if (gw->g.flags & GLIST_FLG_SCROLLSMOOTH)
return;
2014-05-01 03:53:46 +00:00
// Some initial stuff
iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
2014-05-01 03:53:46 +00:00
pgsz = gw->g.height-2;
// Handle click over the scroll bar
if (x >= gw->g.width-(LST_SCROLLWIDTH+2) && (gw2obj->cnt > pgsz/iheight || (gw->g.flags & GLIST_FLG_SCROLLALWAYS))) {
if (y < 2*LST_ARROW_SZ) {
if (gw2obj->top > 0) {
gw2obj->top -= iheight;
if (gw2obj->top < 0)
gw2obj->top = 0;
_gwinUpdate(&gw->g);
}
} else if (y >= gw->g.height - 2*LST_ARROW_SZ) {
if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
gw2obj->top += iheight;
if (gw2obj->top > gw2obj->cnt * iheight - pgsz)
gw2obj->top = gw2obj->cnt * iheight - pgsz;
_gwinUpdate(&gw->g);
}
} else if (y < gw->g.height/2) {
if (gw2obj->top > 0) {
if (gw2obj->top > pgsz)
gw2obj->top -= pgsz;
else
gw2obj->top = 0;
_gwinUpdate(&gw->g);
}
} else {
if (gw2obj->top < gw2obj->cnt * iheight - pgsz) {
if (gw2obj->top < gw2obj->cnt * iheight - 2*pgsz)
gw2obj->top += pgsz;
else
gw2obj->top = gw2obj->cnt * iheight - pgsz;
_gwinUpdate(&gw->g);
}
2013-07-28 13:26:59 +00:00
}
return;
2013-07-25 17:15:51 +00:00
}
ListMouseSelect(gw, x, y);
}
2013-07-25 17:15:51 +00:00
2018-07-08 00:54:19 +00:00
static void ListMouseUp(GWidgetObject* gw, gCoord x, gCoord y) {
// Only act when we are a smooth scrolling list
if (!(gw->g.flags & GLIST_FLG_SCROLLSMOOTH))
return;
// Only allow selection when we did not scroll
if (abs(gw2obj->start_mouse_x - x) > 4 || abs(gw2obj->start_mouse_y - y) > 4)
return;
ListMouseSelect(gw, x, y);
}
2018-07-08 00:54:19 +00:00
static void ListMouseMove(GWidgetObject* gw, gCoord x, gCoord 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, gFontHeight) + LST_VERT_PAD;
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)
_gwinUpdate(&gw->g);
}
2013-07-25 17:15:51 +00:00
}
#endif
2013-07-28 05:00:08 +00:00
#if GINPUT_NEED_TOGGLE
// a toggle-on has occurred
static void ListToggleOn(GWidgetObject *gw, gU16 role) {
const gfxQueueASyncItem * qi;
const gfxQueueASyncItem * qix;
int i;
2018-07-08 00:54:19 +00:00
gCoord iheight;
iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
2013-07-28 05:00:08 +00:00
switch (role) {
2013-07-28 21:18:59 +00:00
// select down
2013-07-28 05:00:08 +00:00
case 0:
for (i = 0, qi = gfxQueueASyncPeek(&gw2obj->list_head); qi; qi = gfxQueueASyncNext(qi), i++) {
2014-11-15 05:21:18 +00:00
if ((qi2li->flags & GLIST_FLG_SELECTED)) {
qix = gfxQueueASyncNext(qi);
if (qix) {
qi2li->flags &=~ GLIST_FLG_SELECTED;
qix2li->flags |= GLIST_FLG_SELECTED;
//if we need to scroll down
if (((i+2)*iheight - gw2obj->top) > gw->g.height){
gw2obj->top += iheight;
}
_gwinUpdate(&gw->g);
}
break;
}
}
2013-07-28 05:00:08 +00:00
break;
2013-07-28 21:18:59 +00:00
// select up
2013-07-28 05:00:08 +00:00
case 1:
qi = gfxQueueASyncPeek(&gw2obj->list_head);
qix = 0;
for (i = 0; qi; qix = qi, qi = gfxQueueASyncNext(qi), i++) {
2015-02-13 23:16:28 +00:00
if ((qi2li->flags & GLIST_FLG_SELECTED)) {
if (qix) {
qi2li->flags &=~ GLIST_FLG_SELECTED;
qix2li->flags |= GLIST_FLG_SELECTED;
//if we need to scroll up
if (((i-1)*iheight) < gw2obj->top){
gw2obj->top -= iheight;
if (gw2obj->top < 0)
gw2obj->top = 0;
}
_gwinUpdate(&gw->g);
}
break;
}
}
2013-07-28 05:00:08 +00:00
break;
}
}
2013-07-28 21:18:59 +00:00
static void ListToggleAssign(GWidgetObject *gw, gU16 role, gU16 instance) {
2013-07-28 21:18:59 +00:00
if (role)
gw2obj->t_up = instance;
2013-07-28 21:18:59 +00:00
else
gw2obj->t_dn = instance;
2013-07-28 21:18:59 +00:00
}
static gU16 ListToggleGet(GWidgetObject *gw, gU16 role) {
return role ? gw2obj->t_up : gw2obj->t_dn;
2013-07-28 21:18:59 +00:00
}
2013-07-28 05:00:08 +00:00
#endif
static void ListDestroy(GHandle gh) {
2013-07-27 23:15:36 +00:00
const gfxQueueASyncItem* qi;
while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
gfxFree((void *)qi);
2013-07-27 20:55:32 +00:00
_gwidgetDestroy(gh);
}
2013-07-17 15:49:21 +00:00
static const gwidgetVMT listVMT = {
{
"List", // The class name
sizeof(GListObject), // The object size
ListDestroy, // The destroy routine
2013-07-17 15:49:21 +00:00
_gwidgetRedraw, // The redraw routine
0, // The after-clear routine
},
gwinListDefaultDraw, // default drawing routine
2013-07-25 17:15:51 +00:00
#if GINPUT_NEED_MOUSE
2013-07-17 15:49:21 +00:00
{
ListMouseDown,
ListMouseUp,
ListMouseMove,
2013-07-17 15:49:21 +00:00
},
#endif
2015-08-16 14:18:54 +00:00
#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
{
0 // Process keyboard events
},
#endif
2013-07-17 15:49:21 +00:00
#if GINPUT_NEED_TOGGLE
{
2013-07-28 05:04:04 +00:00
2, // two toggle roles
ListToggleAssign, // Assign toggles
ListToggleGet, // Get toggles
2013-07-17 15:49:21 +00:00
0,
ListToggleOn, // Process toggle on event
2013-07-17 15:49:21 +00:00
},
#endif
#if GINPUT_NEED_DIAL
{
0,
0,
0,
0,
},
#endif
};
GHandle gwinGListCreate(GDisplay *g, GListObject* gobj, GWidgetInit* pInit, gBool multiselect) {
if (!(gobj = (GListObject *)_gwidgetCreate(g, &gobj->w, pInit, &listVMT)))
2013-07-17 15:49:21 +00:00
return 0;
2013-07-25 17:15:51 +00:00
// initialize the item queue
gfxQueueASyncInit(&gobj->list_head);
gobj->cnt = 0;
gobj->top = 0;
2013-08-01 05:58:46 +00:00
if (multiselect)
gobj->w.g.flags |= GLIST_FLG_MULTISELECT;
2013-10-23 14:26:34 +00:00
gobj->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
2014-04-26 01:27:19 +00:00
gobj->w.g.flags |= GLIST_FLG_ENABLERENDER;
2013-07-25 17:15:51 +00:00
gwinSetVisible(&gobj->w.g, pInit->g.show);
2013-07-17 15:49:21 +00:00
return (GHandle)gobj;
2013-07-17 15:49:21 +00:00
}
void gwinListEnableRender(GHandle gh, gBool ena) {
2014-04-26 01:27:19 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
if (ena) {
gh->flags |= GLIST_FLG_ENABLERENDER;
gwinRedraw(gh);
} else {
gh->flags &=~ GLIST_FLG_ENABLERENDER;
}
}
2013-10-23 14:26:34 +00:00
void gwinListSetScroll(GHandle gh, scroll_t flag) {
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
2013-10-23 14:26:34 +00:00
((GListObject*)gh)->w.g.flags &=~(GLIST_FLG_SCROLLSMOOTH | GLIST_FLG_SCROLLALWAYS);
2013-10-23 14:26:34 +00:00
switch (flag) {
case scrollAlways:
((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLALWAYS;
break;
case scrollAuto:
break;
case scrollSmooth:
((GListObject*)gh)->w.g.flags |= GLIST_FLG_SCROLLSMOOTH;
break;
2013-10-23 14:26:34 +00:00
}
}
int gwinListAddItem(GHandle gh, const char* text, gBool useAlloc) {
ListItem *newItem;
2013-07-25 17:15:51 +00:00
2014-05-01 06:45:26 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return -1;
if (useAlloc) {
gMemSize len = strlen(text)+1;
if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
return -1;
2017-01-09 01:40:48 +00:00
memcpy((char *)(newItem+1), text, len);
text = (const char *)(newItem+1);
} else {
if (!(newItem = gfxAlloc(sizeof(ListItem))))
return -1;
2013-07-25 17:15:51 +00:00
}
// the item is not selected when added
newItem->flags = 0;
newItem->param = 0;
2017-01-09 01:40:48 +00:00
newItem->text = text;
#if GWIN_NEED_LIST_IMAGES
newItem->pimg = 0;
#endif
2013-07-25 17:15:51 +00:00
2013-07-28 05:00:08 +00:00
// select the item if it's the first in the list
2013-08-01 05:58:46 +00:00
if (gh2obj->cnt == 0 && !(gh->flags & GLIST_FLG_MULTISELECT))
2013-07-28 05:00:08 +00:00
newItem->flags |= GLIST_FLG_SELECTED;
2013-07-25 17:15:51 +00:00
// add the new item to the list
gfxQueueASyncPut(&gh2obj->list_head, &newItem->q_item);
2013-07-25 17:15:51 +00:00
// increment the total amount of entries in the list widget
gh2obj->cnt++;
_gwinUpdate(gh);
2013-07-27 20:55:32 +00:00
// return the position in the list (-1 because we start with index 0)
return gh2obj->cnt-1;
2013-07-25 17:15:51 +00:00
}
void gwinListItemSetText(GHandle gh, int item, const char* text, gBool useAlloc) {
2017-01-09 01:40:48 +00:00
const gfxQueueASyncItem *qi;
int i;
ListItem *newItem;
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item > (gh2obj->cnt) - 1)
return;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item) {
// create the new object
if (useAlloc) {
gMemSize len = strlen(text)+1;
2017-01-09 01:40:48 +00:00
if (!(newItem = gfxAlloc(sizeof(ListItem) + len)))
return;
memcpy((char *)(newItem+1), text, len);
text = (const char *)(newItem+1);
} else {
if (!(newItem = gfxAlloc(sizeof(ListItem))))
return;
}
// copy the info from the existing object
newItem->flags = qi2li->flags;
newItem->param = qi2li->param;
newItem->text = text;
#if GWIN_NEED_LIST_IMAGES
newItem->pimg = qi2li->pimg;
#endif
// add the new item to the list and remove the old item
gfxQueueASyncInsert(&gh2obj->list_head, &newItem->q_item, &qi2li->q_item);
gfxQueueASyncRemove(&gh2obj->list_head, &qi2li->q_item);
gfxFree(qi2li);
_gwinUpdate(gh);
break;
}
}
}
const char* gwinListItemGetText(GHandle gh, int item) {
const gfxQueueASyncItem* qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return 0;
// watch out for an invalid item
if (item < 0 || item >= gh2obj->cnt)
2013-07-27 20:55:32 +00:00
return 0;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item)
return qi2li->text;
2013-07-27 20:55:32 +00:00
}
return 0;
2013-07-27 20:55:32 +00:00
}
int gwinListFindText(GHandle gh, const char* text) {
const gfxQueueASyncItem* qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return -1;
// watch out for NULL pointers
if (!text)
return -1;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
2013-07-27 20:55:32 +00:00
if (strcmp(((ListItem *)qi)->text, text) == 0)
return i;
}
return -1;
}
2013-07-25 17:15:51 +00:00
int gwinListGetSelected(GHandle gh) {
const gfxQueueASyncItem * qi;
int i;
2013-07-25 17:15:51 +00:00
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return -1;
2013-07-25 17:15:51 +00:00
2013-08-01 05:58:46 +00:00
// Multi-select always returns -1. Use gwinListItemIsSelected() instead
if ((gh->flags & GLIST_FLG_MULTISELECT))
return -1;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (qi2li->flags & GLIST_FLG_SELECTED)
return i;
}
return -1;
2013-07-25 17:15:51 +00:00
}
void gwinListItemSetParam(GHandle gh, int item, gU16 param) {
const gfxQueueASyncItem * qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item > (gh2obj->cnt) - 1)
2013-07-27 20:55:32 +00:00
return;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item) {
qi2li->param = param;
break;
}
2013-07-27 20:55:32 +00:00
}
}
void gwinListDeleteAll(GHandle gh) {
gfxQueueASyncItem* qi;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
while((qi = gfxQueueASyncGet(&gh2obj->list_head)))
gfxFree(qi);
gh->flags &= ~GLIST_FLG_HASIMAGES;
gh2obj->cnt = 0;
gh2obj->top = 0;
_gwinUpdate(gh);
2013-07-27 20:55:32 +00:00
}
void gwinListItemDelete(GHandle gh, int item) {
const gfxQueueASyncItem * qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item >= gh2obj->cnt)
2013-07-27 20:55:32 +00:00
return;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item) {
gfxQueueASyncRemove(&gh2obj->list_head, (gfxQueueASyncItem*)qi);
gfxFree((void *)qi);
gh2obj->cnt--;
if (gh2obj->top >= item && gh2obj->top)
gh2obj->top--;
_gwinUpdate(gh);
break;
}
2013-07-27 20:55:32 +00:00
}
}
gU16 gwinListItemGetParam(GHandle gh, int item) {
const gfxQueueASyncItem * qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return 0;
// watch out for an invalid item
if (item < 0 || item > (gh2obj->cnt) - 1)
2013-07-27 20:55:32 +00:00
return 0;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item)
return qi2li->param;
2013-07-27 20:55:32 +00:00
}
return 0;
2013-07-27 20:55:32 +00:00
}
gBool gwinListItemIsSelected(GHandle gh, int item) {
const gfxQueueASyncItem * qi;
int i;
2013-07-27 20:55:32 +00:00
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return gFalse;
2013-07-27 20:55:32 +00:00
// watch out for an invalid item
if (item < 0 || item > (gh2obj->cnt) - 1)
return gFalse;
2013-07-27 20:55:32 +00:00
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item)
return (qi2li->flags & GLIST_FLG_SELECTED) ? gTrue : gFalse;
2013-07-27 20:55:32 +00:00
}
return gFalse;
2013-07-27 20:55:32 +00:00
}
int gwinListItemCount(GHandle gh) {
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return 0;
return gh2obj->cnt;
2013-07-27 20:55:32 +00:00
}
2013-10-22 22:18:03 +00:00
const char* gwinListGetSelectedText(GHandle gh) {
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return 0;
// return NULL if nothing is selected (or multi-select)
if (gwinListGetSelected(gh) < 0)
return 0;
return gwinListItemGetText(gh, gwinListGetSelected(gh));
}
void gwinListSetSelected(GHandle gh, int item, gBool doSelect) {
const gfxQueueASyncItem * qi;
int i;
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item >= gh2obj->cnt)
return;
// If not a multiselect mode - clear previous selected item
if (doSelect && !(gh->flags & GLIST_FLG_MULTISELECT)) {
for(qi = gfxQueueASyncPeek(&gh2obj->list_head); qi; qi = gfxQueueASyncNext(qi)) {
if (qi2li->flags & GLIST_FLG_SELECTED) {
qi2li->flags &= ~GLIST_FLG_SELECTED;
break;
}
}
}
// Find item and set selected or not
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item) {
if (doSelect)
qi2li->flags |= GLIST_FLG_SELECTED;
else
qi2li->flags &= ~GLIST_FLG_SELECTED;
break;
}
}
_gwinUpdate(gh);
}
void gwinListViewItem(GHandle gh, int item) {
2018-07-08 00:54:19 +00:00
gCoord iheight;
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item >= gh2obj->cnt)
return;
// Work out a possible new top for the list
iheight = gdispGetFontMetric(gh->font, gFontHeight) + LST_VERT_PAD;
gh2obj->top = iheight * item;
// Adjust the list
if (gh2obj->top > gh2obj->cnt * iheight - gh->height-2)
gh2obj->top = gh2obj->cnt * iheight - gh->height-2;
if (gh2obj->top < 0)
gh2obj->top = 0;
_gwinUpdate(gh);
}
#if GWIN_NEED_LIST_IMAGES
void gwinListItemSetImage(GHandle gh, int item, gImage *pimg) {
const gfxQueueASyncItem * qi;
int i;
// is it a valid handle?
if (gh->vmt != (gwinVMT *)&listVMT)
return;
// watch out for an invalid item
if (item < 0 || item > (gh2obj->cnt) - 1)
return;
for(qi = gfxQueueASyncPeek(&gh2obj->list_head), i = 0; qi; qi = gfxQueueASyncNext(qi), i++) {
if (i == item) {
qi2li->pimg = pimg;
if (pimg)
gh->flags |= GLIST_FLG_HASIMAGES;
break;
}
}
}
#endif
void gwinListDefaultDraw(GWidgetObject* gw, void* param) {
2014-05-01 06:45:26 +00:00
const gfxQueueASyncItem* qi;
int i;
2018-07-08 00:54:19 +00:00
gCoord x, y, iheight, iwidth;
2018-07-08 01:19:43 +00:00
gColor fill;
2014-05-01 06:45:26 +00:00
const GColorSet * ps;
#if GWIN_NEED_LIST_IMAGES
2018-07-08 00:54:19 +00:00
gCoord sy;
2014-05-01 06:45:26 +00:00
#endif
#if GDISP_NEED_CONVEX_POLYGON
2018-07-08 00:43:30 +00:00
static const gPoint upArrow[] = { {0, LST_ARROW_SZ}, {LST_ARROW_SZ, LST_ARROW_SZ}, {LST_ARROW_SZ/2, 0} };
static const gPoint downArrow[] = { {0, 0}, {LST_ARROW_SZ, 0}, {LST_ARROW_SZ/2, LST_ARROW_SZ} };
#endif
(void)param;
2014-05-01 06:45:26 +00:00
// is it a valid handle?
if (gw->g.vmt != (gwinVMT *)&listVMT)
return;
// don't render if render has been disabled
if (!(gw->g.flags & GLIST_FLG_ENABLERENDER))
return;
ps = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled;
iheight = gdispGetFontMetric(gw->g.font, gFontHeight) + LST_VERT_PAD;
2014-05-01 06:45:26 +00:00
x = 1;
// the scroll area
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 - (LST_SCROLLWIDTH+3);
gdispGFillArea(gw->g.display, gw->g.x+iwidth+2, gw->g.y+1, LST_SCROLLWIDTH, gw->g.height-2, gdispBlendColor(ps->fill, gw->pstyle->background, 128));
2014-05-01 06:45:26 +00:00
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);
#if GDISP_NEED_CONVEX_POLYGON
gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+(LST_ARROW_SZ/2+1), upArrow, 3, ps->fill);
gdispGFillConvexPoly(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+gw->g.height-(LST_ARROW_SZ+LST_ARROW_SZ/2+1), downArrow, 3, ps->fill);
2014-05-01 06:45:26 +00:00
#else
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
#warning "GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on"
#elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
COMPILER_WARNING("GWIN: Lists display better when GDISP_NEED_CONVEX_POLYGON is turned on")
#endif
gdispGFillArea(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+(LST_ARROW_SZ/2+1), LST_ARROW_SZ, LST_ARROW_SZ, ps->fill);
gdispGFillArea(gw->g.display, gw->g.x+iwidth+((LST_SCROLLWIDTH-LST_ARROW_SZ)/2+2), gw->g.y+gw->g.height-(LST_ARROW_SZ+LST_ARROW_SZ/2+1), LST_ARROW_SZ, LST_ARROW_SZ, ps->fill);
2014-05-01 06:45:26 +00:00
#endif
} else
iwidth = gw->g.width - 2;
#if GWIN_NEED_LIST_IMAGES
if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
x += iheight;
iwidth -= iheight;
}
#endif
// Find the top item
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.
#if GDISP_NEED_CLIP
gdispGSetClip(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2);
#endif
2014-05-01 06:45:26 +00:00
// Draw until we run out of room or items
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;
gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, iwidth, iheight, fill);
#if GWIN_NEED_LIST_IMAGES
if ((gw->g.flags & GLIST_FLG_HASIMAGES)) {
// Clear the image area
if (qi2li->pimg && gdispImageIsOpen(qi2li->pimg)) {
// Calculate which image
sy = (qi2li->flags & GLIST_FLG_SELECTED) ? 0 : (iheight-LST_VERT_PAD);
if (!(gw->g.flags & GWIN_FLG_SYSENABLED))
sy += 2*(iheight-LST_VERT_PAD);
2014-05-01 06:45:26 +00:00
while (sy > qi2li->pimg->height)
sy -= iheight-LST_VERT_PAD;
2014-05-01 06:45:26 +00:00
// Draw the image
gdispImageSetBgColor(qi2li->pimg, fill);
gdispGImageDraw(gw->g.display, qi2li->pimg, gw->g.x+1, gw->g.y+y, iheight-LST_VERT_PAD, iheight-LST_VERT_PAD, 0, sy);
2014-05-01 06:45:26 +00:00
}
}
#endif
gdispGFillStringBox(gw->g.display, gw->g.x+x+LST_HORIZ_PAD, gw->g.y+y, iwidth-LST_HORIZ_PAD, iheight, qi2li->text, gw->g.font, ps->text, fill, gJustifyLeft);
2014-05-01 06:45:26 +00:00
}
// 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);
}
#undef gh2obj
#undef gw2obj
#undef qi2li
#undef qix2li
#undef ple
2013-07-17 15:49:21 +00:00
#endif // GFX_USE_GWIN && GWIN_NEED_LIST