From 128a3b972cc50b084415b5fb69fc355fe5db9520 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sat, 3 Jan 2015 18:46:46 +1000 Subject: New Tabset Widget. Widgets demo updated to (optionally) use the new tabset. --- src/gwin/gwin_container.h | 3 + src/gwin/gwin_tabset.c | 562 ++++++++++++++++++++++++++++++++++++++++++++++ src/gwin/gwin_tabset.h | 206 +++++++++++++++++ src/gwin/sys_make.mk | 1 + src/gwin/sys_options.h | 14 ++ src/gwin/sys_rules.h | 2 +- 6 files changed, 787 insertions(+), 1 deletion(-) create mode 100644 src/gwin/gwin_tabset.c create mode 100644 src/gwin/gwin_tabset.h (limited to 'src') diff --git a/src/gwin/gwin_container.h b/src/gwin/gwin_container.h index ff1c1ce9..19562a7d 100644 --- a/src/gwin/gwin_container.h +++ b/src/gwin/gwin_container.h @@ -156,6 +156,9 @@ extern "C" { #if GWIN_NEED_FRAME || defined(__DOXYGEN__) #include "gwin_frame.h" #endif +#if GWIN_NEED_TABSET || defined(__DOXYGEN__) + #include "gwin_tabset.h" +#endif #endif /* _GCONTAINER_H */ /** @} */ diff --git a/src/gwin/gwin_tabset.c b/src/gwin/gwin_tabset.c new file mode 100644 index 00000000..a96bcbaf --- /dev/null +++ b/src/gwin/gwin_tabset.c @@ -0,0 +1,562 @@ +/* + * 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: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin_frame.c + * @brief GWIN sub-system frame code. + */ + +#include "gfx.h" + +#if GFX_USE_GWIN && GWIN_NEED_TABSET + +#include "gwin_class.h" + +// Some position values +#define BORDER_WIDTH 2 +#define TEXT_PADDING 5 + +// Some color blending +#define GTABSET_TAB_CNR 8 // Diagonal corner on active tab +#define GTABSET_TOP_FADE 50 // (GTABSET_TOP_FADE/255)% fade to white for top of tab/button +#define GTABSET_BOTTOM_FADE 25 // (GTABSET_BOTTOM_FADE/255)% fade to black for bottom of tab/button +#define GTABSET_OUTLINE_FADE 128 // (GTABSET_OUTLINE_FADE/255)% fade to background for active tab edge + +/* Internal state flags */ +#define GWIN_TABSET_USER_FLAGS (GWIN_TABSET_BORDER) +#if GWIN_TABSET_BORDER < GWIN_FIRST_CONTROL_FLAG + #error "GWIN Tabset: - Flag definitions don't match" +#endif +#if GWIN_TABSET_BORDER > GWIN_LAST_CONTROL_FLAG + #error "GWIN Tabset: - Flag definitions don't match" +#endif + +/******************************************************************************************************************** + * Tab-page stuff + */ + +static void FixTabSizePos(GHandle gh); + +typedef GContainerObject GTabpageObject; + +static coord_t TabpageBorderSize(GHandle gh) { (void)gh; return 0; } + +static void gwinTabpageDraw_Std(GWidgetObject *gw, void *param) { + (void)gw; + (void)param; + + // The page is effectively transparent +} + +static void TabpageDestroy(GHandle gh) { + _gcontainerDestroy(gh); + + FixTabSizePos(gh->parent); +} + +static const gcontainerVMT tabpageVMT = { + { + { + "Tabpage", // The classname + sizeof(GTabpageObject), // The object size + TabpageDestroy, // The destroy routine + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinTabpageDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + 0, // Process mouse down event + 0, // Process mouse up events + 0, // Process mouse move events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // 1 toggle role + 0, // Assign Toggles + 0, // Get Toggles + 0, // Process toggle off events + 0, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // 1 dial roles + 0, // Assign Dials + 0, // Get Dials + 0, // Process dial move events + }, + #endif + }, + TabpageBorderSize, // The size of the left border (mandatory) + TabpageBorderSize, // The size of the top border (mandatory) + TabpageBorderSize, // The size of the right border (mandatory) + TabpageBorderSize, // The size of the bottom border (mandatory) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +void gwinTabsetSetTitle(GHandle gh, const char *title, bool_t useAlloc) { + if (gh->vmt != (gwinVMT *)&tabpageVMT) + return; + + gwinSetText(gh, title, useAlloc); + FixTabSizePos(gh->parent); + gwinRedraw(gh->parent); +} + +/******************************************************************************************************************** + * Tab-set stuff + */ + +static coord_t CalcTabHeight(GHandle gh) { + GHandle ph; + coord_t x, y, w; + + x = w = 0; + y = GWIN_TABSET_TABHEIGHT; + for(ph = gwinGetFirstChild(gh); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT) { + w = gdispGetStringWidth(((GWidgetObject *)ph)->text, gh->font) + TEXT_PADDING*2; + x += w; + if (x > gh->width) { + y += GWIN_TABSET_TABHEIGHT; + x = w; + } + } + } + return y; +} + +static void FixTabSizePos(GHandle gh) { + coord_t w, h, oldth; + GHandle vis, ph; + + oldth = ((GTabsetObject *)gh)->border_top; + ((GTabsetObject *)gh)->border_top = CalcTabHeight(gh); + oldth -= ((GTabsetObject *)gh)->border_top; + if (oldth == 0) + return; + + vis = 0; + w = gwinGetInnerWidth(gh); + h = gwinGetInnerHeight(gh); + for(ph = gwinGetFirstChild(gh); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT) { + if (!vis || (ph->flags & GWIN_FLG_VISIBLE)) + vis = ph; + gwinMove(ph, 0, 0); + gwinResize(ph, w, h); + } else { + gwinMove(ph, ph->x-gh->x-((gh->flags & GWIN_TABSET_BORDER) ? BORDER_WIDTH : 0) , ph->y-oldth-gh->y); + } + } + if (vis && !(vis->flags & GWIN_FLG_VISIBLE)) { + vis->flags |= GWIN_FLG_VISIBLE; + _gwinRippleVisibility(); + } +} + +static coord_t TabSetBorderSize(GHandle gh) { return (gh->flags & GWIN_TABSET_BORDER) ? BORDER_WIDTH : 0; } +static coord_t TabSetBorderTop(GHandle gh) { return ((GTabsetObject *)gh)->border_top; } + +#if GINPUT_NEED_MOUSE + static void mouseDown(GWidgetObject *gw, coord_t mx, coord_t my) { + GHandle ph, gh; + int cnt; + + if (my < 0 || my > ((GTabsetObject *)gw)->border_top) + return; + + // Work out which tab was pressed + { + coord_t x, w, y; + + cnt = 0; + x = w = 0; + y = GWIN_TABSET_TABHEIGHT; + gh = 0; + for(ph = gwinGetFirstChild(&gw->g); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT) { + w = gdispGetStringWidth(((GWidgetObject *)ph)->text, gw->g.font) + TEXT_PADDING*2; + x += w; + if (x > gw->g.width) { + y += GWIN_TABSET_TABHEIGHT; + x = w; + } + if (my < y && mx < x) { + gh = ph; + break; + } + cnt++; + } + } + if (!gh || (gh->flags & GWIN_FLG_VISIBLE)) + return; + } + + // Mark the existing tab as not visible + for(ph = gwinGetFirstChild(&gw->g); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT && (ph->flags & GWIN_FLG_VISIBLE)) { + // Mark this page invisible + ph->flags &= ~GWIN_FLG_VISIBLE; + break; + } + } + + // Mark this tab as visible + gh->flags |= GWIN_FLG_VISIBLE; + _gwinRippleVisibility(); + + // Force a redraw of the whole tabset + _gwinUpdate(&gw->g); + + // Send the Tabset Event + { + GSourceListener * psl; + GEventGWinTabset * pge; + + psl = 0; + while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { + if (!(pge = (GEventGWinTabset *)geventGetEventBuffer(psl))) + continue; + pge->type = GEVENT_GWIN_TABSET; + pge->gwin = &gw->g; + #if GWIN_WIDGET_TAGS + pge->tag = gw->tag; + #endif + pge->ghPage = gh; + pge->nPage = cnt; + geventSendEvent(psl); + } + } + } +#endif + +static const gcontainerVMT tabsetVMT = { + { + { + "Tabset", // The classname + sizeof(GTabsetObject), // The object size + _gcontainerDestroy, // The destroy routine + _gcontainerRedraw, // The redraw routine + 0, // The after-clear routine + }, + gwinTabsetDraw_Std, // The default drawing routine + #if GINPUT_NEED_MOUSE + { + mouseDown, // Process mouse down event + 0, // Process mouse up events + 0, // Process mouse move events + }, + #endif + #if GINPUT_NEED_TOGGLE + { + 0, // 1 toggle role + 0, // Assign Toggles + 0, // Get Toggles + 0, // Process toggle off events + 0, // Process toggle on events + }, + #endif + #if GINPUT_NEED_DIAL + { + 0, // 1 dial roles + 0, // Assign Dials + 0, // Get Dials + 0, // Process dial move events + }, + #endif + }, + TabSetBorderSize, // The size of the left border (mandatory) + TabSetBorderTop, // The size of the top border (mandatory) + TabSetBorderSize, // The size of the right border (mandatory) + TabSetBorderSize, // The size of the bottom border (mandatory) + 0, // A child has been added (optional) + 0, // A child has been deleted (optional) +}; + +GHandle gwinGTabsetCreate(GDisplay *g, GTabsetObject *fo, GWidgetInit *pInit, uint32_t flags) { + if (!(fo = (GTabsetObject *)_gcontainerCreate(g, (GContainerObject *)fo, pInit, &tabsetVMT))) + return 0; + + // Set Tabset specific stuff + fo->c.g.flags |= flags & GWIN_TABSET_USER_FLAGS; + fo->border_top = GWIN_TABSET_TABHEIGHT; + + gwinSetVisible(&fo->c.g, pInit->g.show); + + return &fo->c.g; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// API calls +/////////////////////////////////////////////////////////////////////////////////////////////////// + +GHandle gwinTabsetAddTab(GHandle gh, const char *title, bool_t useAlloc) { + GWidgetInit wi; + + if (gh->vmt != (gwinVMT *)&tabsetVMT) + return 0; + + // Set up the init structure + gwinWidgetClearInit(&wi); + wi.g.x = wi.g.y = 0; + wi.g.width = gwinGetInnerWidth(gh); + wi.g.height = gwinGetInnerHeight(gh); + wi.g.show = !gwinTabsetCountTabs(gh); + wi.g.parent = gh; + + // Create the page + if (!(gh = _gcontainerCreate(gh->display, 0, &wi, &tabpageVMT))) + return 0; + + // Set the text and visibility + gwinSetText(gh, title, useAlloc); + FixTabSizePos(gh->parent); + + gwinSetVisible(gh, wi.g.show); + gwinRedraw(gh->parent); + return gh; +} + +int gwinTabsetCountTabs(GHandle gh) { + int cnt; + + if (gh->vmt != (gwinVMT *)&tabsetVMT) + return 0; + + for(cnt = 0, gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) { + if (gh->vmt == (gwinVMT *)&tabpageVMT) + cnt++; + } + return cnt; +} + +GHandle gwinTabsetGetTabByIndex(GHandle gh, int index) { + if (gh->vmt != (gwinVMT *)&tabsetVMT) + return 0; + + for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) { + if (gh->vmt == (gwinVMT *)&tabpageVMT && !index--) + return gh; + } + return 0; +} + +GHandle gwinTabsetGetTabByTitle(GHandle gh, const char *title) { + if (gh->vmt != (gwinVMT *)&tabsetVMT) + return 0; + + for(gh = gwinGetFirstChild(gh); gh; gh = gwinGetSibling(gh)) { + if (gh->vmt == (gwinVMT *)&tabpageVMT && !strcmp(title, ((GWidgetObject *)gh)->text)) + return gh; + } + return 0; +} + +void gwinTabsetSetTab(GHandle gh) { + GHandle ph; + + if (gh->vmt != (gwinVMT *)&tabpageVMT || (gh->flags & GWIN_FLG_VISIBLE)) + return; + + // We alter the visibility flags here directly as we know we are going to redraw everything + for(ph = gwinGetFirstChild(gh->parent); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT && (ph->flags & GWIN_FLG_VISIBLE)) { + // Mark this page invisible + ph->flags &= ~GWIN_FLG_VISIBLE; + break; + } + } + + // Mark this tab as visible + gh->flags |= GWIN_FLG_VISIBLE; + _gwinRippleVisibility(); + + // Force a redraw of the tabset + gwinRedraw(gh->parent); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Default render routines // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if GWIN_FLAT_STYLING + static void fgarea(GWidgetObjset *gw, const char *text, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->pressed : &gw->pstyle->disabled; + + gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, w, GWIN_TABSET_TABHEIGHT, pcol->edge); + gdispGFillStringBox(gw->g.display, gw->g.x+x+1, gw->g.y+y+1, w-2, GWIN_TABSET_TABHEIGHT-1, text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + } + static void bgarea(GWidgetObjset *gw, const char *text, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + + gdispGFillStringBox(gw->g.display, gw->g.x+x, gw->g.y+y, w-1, GWIN_TABSET_TABHEIGHT, text, gw->g.font, pcol->text, pcol->fill, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+x+w-1, gw->g.y+y, gw->g.x+x+w-1, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x+x, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, gw->g.x+x+w-2, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, pcol->edge); + } + static void ntarea(GWidgetObjset *gw, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->pressed : &gw->pstyle->disabled; + + gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y, w+y, GWIN_TABSET_TABHEIGHT-1, gw->g.bgcolor); + gdispGDrawLine(gw->g.display, gw->g.x+x, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, gw->g.x+x+w-1, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, pcol->edge); + } +#else + static void fgarea(GWidgetObject *gw, const char *text, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + color_t tcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->pressed : &gw->pstyle->disabled; + + tcol = gdispBlendColor(pcol->edge, gw->pstyle->background, GTABSET_OUTLINE_FADE); + gdispGFillStringBox(gw->g.display, gw->g.x+x, gw->g.y+y, w, GWIN_TABSET_TABHEIGHT, text, gw->g.font, pcol->text, gw->g.bgcolor, justifyCenter); + gdispGDrawLine(gw->g.display, gw->g.x+x, gw->g.y+y, gw->g.x+x+w-(GTABSET_TAB_CNR+1), gw->g.y+y, tcol); + gdispGDrawLine(gw->g.display, gw->g.x+x+w-(GTABSET_TAB_CNR+1), gw->g.y+y, gw->g.x+x+w-1, gw->g.y+y+GTABSET_TAB_CNR, tcol); + gdispGDrawLine(gw->g.display, gw->g.x+x+w-1, gw->g.y+y+GTABSET_TAB_CNR, gw->g.x+x+w-1, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, tcol); + if (!x) + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+y, gw->g.x, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, tcol); + } + static void bgarea(GWidgetObject *gw, const char *text, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + fixed alpha; + coord_t i; + color_t tcol, bcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + + /* Fill the box blended from variants of the fill color */ + tcol = gdispBlendColor(White, pcol->fill, GTABSET_TOP_FADE); + bcol = gdispBlendColor(Black, pcol->fill, GTABSET_BOTTOM_FADE); + for(alpha = 0, i = 0; i < GWIN_TABSET_TABHEIGHT; i++, alpha += FIXED(255)/GWIN_TABSET_TABHEIGHT) + gdispGDrawLine(gw->g.display, gw->g.x+x, gw->g.y+y+i, gw->g.x+x+w-2, gw->g.y+y+i, gdispBlendColor(bcol, tcol, NONFIXED(alpha))); + gdispGDrawLine(gw->g.display, gw->g.x+x+w-1, gw->g.y+y, gw->g.x+x+w-1, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, pcol->edge); + gdispGDrawStringBox(gw->g.display, gw->g.x+x+1, gw->g.y+y+1, w-2, GWIN_TABSET_TABHEIGHT-2, text, gw->g.font, pcol->text, justifyCenter); + } + static void ntarea(GWidgetObject *gw, coord_t y, coord_t x, coord_t w) { + const GColorSet * pcol; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->pressed : &gw->pstyle->disabled; + + gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, w, GWIN_TABSET_TABHEIGHT-1, gw->g.bgcolor); + gdispGDrawLine(gw->g.display, gw->g.x+x, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, gw->g.x+x+w-1, gw->g.y+y+GWIN_TABSET_TABHEIGHT-1, pcol->edge); + } +#endif + +static coord_t drawtabs(GWidgetObject *gw) { + GHandle ph; + coord_t x, y, w; + + x = w = 0; + y = 0; + for(ph = gwinGetFirstChild(&gw->g); ph; ph = gwinGetSibling(ph)) { + if (ph->vmt == (gwinVMT *)&tabpageVMT) { + w = gdispGetStringWidth(((GWidgetObject *)ph)->text, gw->g.font) + TEXT_PADDING*2; + if (x+w > gw->g.width) { + ntarea(gw, y, x, gw->g.width - x); + y += GWIN_TABSET_TABHEIGHT; + x = 0; + } + if (ph->flags & GWIN_FLG_VISIBLE) + fgarea(gw, ((GWidgetObject *)ph)->text, y, x, w); + else + bgarea(gw, ((GWidgetObject *)ph)->text, y, x, w); + x += w; + } + } + if (x < gw->g.width) + ntarea(gw, y, x, gw->g.width - x); + return y + GWIN_TABSET_TABHEIGHT; +} + +static void drawborder(GWidgetObject *gw, coord_t y) { + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) { + const GColorSet * pcol; + coord_t x, w; + + pcol = (gw->g.flags & GWIN_FLG_SYSENABLED) ? &gw->pstyle->enabled : &gw->pstyle->disabled; + x = gw->g.x+gw->g.width-1; + w = gw->g.y+gw->g.height-1; + gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+y, gw->g.x, w-1, pcol->edge); + gdispGDrawLine(gw->g.display, gw->g.x, w, x, w, pcol->edge); + gdispGDrawLine(gw->g.display, x, gw->g.y+y, x, w-1, pcol->edge); + } +} + +void gwinTabsetDraw_Transparent(GWidgetObject *gw, void *param) { + (void) param; + + if (gw->g.vmt != (gwinVMT *)&tabsetVMT) + return; + + drawborder(gw, drawtabs(gw)); + + // Don't touch the client area +} + +void gwinTabsetDraw_Std(GWidgetObject *gw, void *param) { + coord_t y; + (void) param; + + if (gw->g.vmt != (gwinVMT *)&tabsetVMT) + return; + + // Draw the frame + y = drawtabs(gw); + drawborder(gw, y); + + // Draw the client area + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) + gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+y, gw->g.width-2, gw->g.height-y-1, gw->pstyle->background); + else + gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+y, gw->g.width, gw->g.height-y, gw->pstyle->background); +} + +#if GDISP_NEED_IMAGE + void gwinTabsetDraw_Image(GWidgetObject *gw, void *param) { + #define gi ((gdispImage *)param) + coord_t x, y, iw, ih, mx, my; + + if (gw->g.vmt != (gwinVMT *)&tabsetVMT) + return; + + // Draw the frame + y = drawtabs(gw); + drawborder(gw, y); + + // Draw the client area by tiling the image + mx = gw->g.x+gw->g.width; + my = gw->g.y+gw->g.height; + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) { + mx -= 2; + my -= 1; + } + for(y = gw->g.y+y, ih = gi->height; y < my; y += ih) { + if (ih > my - y) + ih = my - y; + x = gw->g.x; + if ((gw->g.flags & GWIN_CONTAINER_BORDER)) + x++; + for(iw = gi->width; x < mx; x += iw) { + if (iw > mx - x) + iw = mx - x; + gdispGImageDraw(gw->g.display, gi, x, y, ih, iw, 0, 0); + } + } + + #undef gi + } +#endif + +#endif /* (GFX_USE_GWIN && GWIN_NEED_TABSET) || defined(__DOXYGEN__) */ diff --git a/src/gwin/gwin_tabset.h b/src/gwin/gwin_tabset.h new file mode 100644 index 00000000..f1d98adb --- /dev/null +++ b/src/gwin/gwin_tabset.h @@ -0,0 +1,206 @@ +/* + * 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: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gwin/gwin_tabset.h + * @brief GWIN Graphic window subsystem header file. + * + * @defgroup Tabset Tabset + * @ingroup Containers + * + * @details A tabset is a set of tabs that control visibility of a number of pages of widgets. + * Note: Although the tabset is implemented as a container - you don't put your controls + * directly on the tabset. Instead you create a page and put your widgets on the page. + * + * @pre GFX_USE_GWIN must be set to TRUE in your gfxconf.h + * @pre GWIN_NEED_TABSET must be set to TRUE in your gfxconf.h + * @{ + */ + +#ifndef _GWIN_TABSET_H +#define _GWIN_TABSET_H + +/* This file is included from src/gwin/gwin_container.h */ + +/** + * @brief The Event Type for a Tabset Event + */ +#define GEVENT_GWIN_TABSET (GEVENT_GWIN_CTRL_FIRST+5) + +/** + * @brief A Tabset Event + * @note There are currently no GEventGWinTabset listening flags - use 0 as the flags to @p gwinAttachListener() + */ +typedef struct GEventGWinTabset { + GEventType type; // The type of this event (GEVENT_GWIN_TABSET) + GHandle gwin; // The tabset window handle + #if GWIN_NEED_WIDGET && GWIN_WIDGET_TAGS + WidgetTag tag; // The tag of the tabset + #endif + // Above are the generic widget event elements, below the tabset specific elements + GHandle ghPage; // The tabpage window handle that has been selected + int nPage; // The page number (0 to n-1) that has been selected + } GEventGWinTabset; + +/** + * @brief Flags for gwinTabsetCreate() + * @{ + */ +#define GWIN_TABSET_BORDER 0x00000001 // Should the tab pages have a border? +/** @} */ + +typedef struct GTabsetObject { + GContainerObject c; + coord_t border_top; + } GTabsetObject; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Create a tabset widget + * + * @details This widget provides a set of tabs. + * + * @param[in] g The GDisplay to display this window on + * @param[in] fo The GTabsetObject structure to initialize. If this is NULL the structure is dynamically allocated. + * @param[in] pInit The initialization parameters + * @param[in] flags Some flags, see notes. + * + * @note Possible flags are: GWIN_TABSET_BORDER + * + * @return NULL if there is no resulting widget. A valid GHandle otherwise. + * + * @api + */ + GHandle gwinGTabsetCreate(GDisplay *g, GTabsetObject *fo, GWidgetInit *pInit, uint32_t flags); + #define gwinTabsetCreate(fo, pInit, flags) gwinGTabsetCreate(GDISP, fo, pInit, flags); + + /** + * @brief Add a tab-page to the tabset + * @returns The GHandle of the tab-page container. + * + * @param[in] gh The tabset handle + * @param[in] title The text to set. This must be a constant string unless useAlloc is set. + * @param[in] useAlloc If TRUE the string specified will be copied into dynamically allocated memory. + * + * @api + */ + GHandle gwinTabsetAddTab(GHandle gh, const char *title, bool_t useAlloc); + + /** + * @brief Delete a tab-page. + * @details Any widgets on the page will also be destroyed + * + * @param[in] gh The tab-page handle + * + * @note The index position of all tabs after this tab in the tabset are automatically renumbered. + * + * @api + */ + #define gwinTabsetDeleteTab(gh) gwinDestroy(gh) + + /** + * @brief Count the number of tabs in the tabset + * @returns The number of tabs or zero if none exist. + * + * @param[in] gh The tabset handle + * + * @api + */ + int gwinTabsetCountTabs(GHandle gh); + + /** + * @brief Get the GHandle of a tab based on its position + * @returns The GHandle of the tab-page container or NULL if that tab-page doesn't exist. + * + * @param[in] gh The tabset handle + * @param[in] index The tab-page handle to return (0 to number of pages - 1) + * + * @api + */ + GHandle gwinTabsetGetTabByIndex(GHandle gh, int index); + + /** + * @brief Get the GHandle of a tab based on its title + * @returns The GHandle of the tab-page container or NULL if that tab-page doesn't exist. + * + * @param[in] gh The tabset handle + * @param[in] title The title to search for + * + * @api + */ + GHandle gwinTabsetGetTabByTitle(GHandle gh, const char *title); + + /** + * @brief Set the title of a tab-page. + * + * @param[in] gh The tab-page handle (NB: Use the page handle NOT the tabset handle) + * @param[in] title The text to set. This must be a constant string unless useAlloc is set. + * @param[in] useAlloc If TRUE the string specified will be copied into dynamically allocated memory. + * + * @note This function should be used to change the text associated with a tab-page + * rather than @p gwinSetText(). + * + * @api + */ + void gwinTabsetSetTitle(GHandle gh, const char *title, bool_t useAlloc); + + /** + * @brief Get the title of a tab-page. + * @return The title of the tab. + * + * @param[in] gh The tab-page handle (NB: Use the page handle NOT the tabset handle) + * + * @api + */ + #define gwinTabsetGetTitle(gh) gwinGetText(gh) + + /** + * @brief Set the active tab in a tabset. + * + * @param[in] gh The tab-page handle (NB: Use the page handle NOT the tabset handle) + * + * @api + */ + void gwinTabsetSetTab(GHandle gh); + + /** + * @brief The custom draw routines for a frame window + * @details These function may be passed to @p gwinSetCustomDraw() to get different frame drawing styles + * + * @param[in] gw The widget object (in this case a frame) + * @param[in] param A parameter passed in from the user + * + * @note In your own custom drawing function you may optionally call these + * standard functions and then draw your extra details on top. + * + * @note gwinTabsetDraw_Std() will fill the client area with the background color.
+ * gwinTabsetDraw_Transparent() will not fill the client area at all.
+ * gwinTabsetDraw_Image() will tile the image throughout the client area.
+ * All these drawing functions draw the frame itself the same way. + * + * @note The standard functions below ignore the param parameter except for @p gwinTabsetDraw_Image(). + * @note The image custom draw function @p gwinTabsetDraw_Image() uses param to pass in the gdispImage pointer. + * The image must be already opened before calling @p gwinSetCustomDraw(). + * + * @api + * @{ + */ + void gwinTabsetDraw_Std(GWidgetObject *gw, void *param); + void gwinTabsetDraw_Transparent(GWidgetObject *gw, void *param); + void gwinTabsetDraw_Image(GWidgetObject *gw, void *param); + /** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GWIN_TABSET_H */ +/** @} */ + diff --git a/src/gwin/sys_make.mk b/src/gwin/sys_make.mk index 81277a4e..c619466e 100644 --- a/src/gwin/sys_make.mk +++ b/src/gwin/sys_make.mk @@ -13,6 +13,7 @@ GFXSRC += $(GFXLIB)/src/gwin/gwin_gwin.c \ $(GFXLIB)/src/gwin/gwin_progressbar.c \ $(GFXLIB)/src/gwin/gwin_container.c \ $(GFXLIB)/src/gwin/gwin_frame.c \ + $(GFXLIB)/src/gwin/gwin_tabset.c \ $(GFXLIB)/src/gwin/gwin_gl3d.c \ GFXINC += $(GFXLIB)/3rdparty/tinygl-0.4-ugfx/include diff --git a/src/gwin/sys_options.h b/src/gwin/sys_options.h index bb7e3598..df8f497e 100644 --- a/src/gwin/sys_options.h +++ b/src/gwin/sys_options.h @@ -128,6 +128,13 @@ #ifndef GWIN_NEED_RADIO #define GWIN_NEED_RADIO FALSE #endif + /** + * @brief Should tabset functions be included. + * @details Defaults to FALSE + */ + #ifndef GWIN_NEED_TABSET + #define GWIN_NEED_TABSET FALSE + #endif /** * @} * @@ -314,6 +321,13 @@ #ifndef GWIN_SLIDER_TOGGLE_INC #define GWIN_SLIDER_TOGGLE_INC 20 #endif + /** + * @brief The height in pixels of a row of tabs in a tabset + * @details Defaults to 18 + */ + #ifndef GWIN_TABSET_TABHEIGHT + #define GWIN_TABSET_TABHEIGHT 18 + #endif /** @} */ #endif /* _GWIN_OPTIONS_H */ diff --git a/src/gwin/sys_rules.h b/src/gwin/sys_rules.h index 911379af..39864901 100644 --- a/src/gwin/sys_rules.h +++ b/src/gwin/sys_rules.h @@ -28,7 +28,7 @@ #endif // Objects require their super-class - #if GWIN_NEED_FRAME || GWIN_NEED_CONTAINER + #if GWIN_NEED_TABSET || GWIN_NEED_FRAME || GWIN_NEED_CONTAINER #if !GWIN_NEED_CONTAINERS #if GFX_DISPLAY_RULE_WARNINGS #warning "GWIN: GWIN_NEED_CONTAINERS is required when a container is enabled. It has been turned on for you." -- cgit v1.2.3