diff --git a/demos/modules/gwin/widgets/gfxconf.h b/demos/modules/gwin/widgets/gfxconf.h index 346a9898..e7e2714e 100644 --- a/demos/modules/gwin/widgets/gfxconf.h +++ b/demos/modules/gwin/widgets/gfxconf.h @@ -75,6 +75,8 @@ #define GWIN_NEED_CONTAINERS TRUE #define GWIN_NEED_CONTAINER TRUE +#define GWIN_NEED_TABSET TRUE + /////////////////////////////////////////////////////////////////////////// // GEVENT // /////////////////////////////////////////////////////////////////////////// diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index d2f6882b..ea3dbfba 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -40,6 +40,12 @@ * The ROMFS uses the file "romfs_files.h" to describe the set of files in the ROMFS. */ +/** + * The code can either use the Tabset control or use Radio buttons set to the Tab style. + * Change this in your gfxconf.h file by defining GWIN_NEED_TABSET (or not). It is + * defined by default in this demo. + */ + /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background @@ -73,7 +79,11 @@ static const GWidgetStyle YellowWidgetStyle = { static font_t font; static GListener gl; static GHandle ghConsole; -static GHandle ghTabButtons, ghTabSliders, ghTabCheckboxes, ghTabLabels, ghTabRadios, ghTabLists, ghTabImages, ghTabProgressbar; +#if GWIN_NEED_TABSET + static GHandle ghTabset; +#else + static GHandle ghTabButtons, ghTabSliders, ghTabCheckboxes, ghTabLabels, ghTabRadios, ghTabLists, ghTabImages, ghTabProgressbar; +#endif static GHandle ghPgButtons, ghPgSliders, ghPgCheckboxes, ghPgLabels, ghPgRadios, ghPgLists, ghPgImages, ghPgProgressbars; static GHandle ghButton1, ghButton2, ghButton3, ghButton4; static GHandle ghSlider1, ghSlider2, ghSlider3, ghSlider4; @@ -108,21 +118,56 @@ static gdispImage imgYesNo; #define GROUP_YESNO 1 #define GROUP_COLORS 2 -// Wrap tabs onto the next line if they don't fit. -static void settabtext(GWidgetInit *pwi, char *txt) { - if (pwi->g.x >= ScrWidth) { - pwi->g.x = 0; - pwi->g.y += pwi->g.height; +#if !GWIN_NEED_TABSET + // Wrap tabs onto the next line if they don't fit. + static void settabtext(GWidgetInit *pwi, char *txt) { + if (pwi->g.x >= ScrWidth) { + pwi->g.x = 0; + pwi->g.y += pwi->g.height; + } + pwi->text = txt; + pwi->g.width = gdispGetStringWidth(pwi->text, font) + BUTTON_PADDING; + if (pwi->g.x + pwi->g.width > ScrWidth) { + pwi->g.x = 0; + pwi->g.y += pwi->g.height; + } } - pwi->text = txt; - pwi->g.width = gdispGetStringWidth(pwi->text, font) + BUTTON_PADDING; - if (pwi->g.x + pwi->g.width > ScrWidth) { - pwi->g.x = 0; - pwi->g.y += pwi->g.height; - } -} -// Wrap tabs onto the next line if they don't fit. + /** + * Set the visibility of widgets based on which tab is selected. + */ + static void setTab(GHandle tab) { + /* Make sure everything is invisible first */ + gwinHide(ghPgButtons); + gwinHide(ghPgSliders); + gwinHide(ghPgCheckboxes); + gwinHide(ghPgLabels); + gwinHide(ghPgRadios); + gwinHide(ghPgLists); + gwinHide(ghPgImages); + gwinHide(ghPgProgressbars); + + /* Turn on widgets depending on the tab selected */ + if (tab == ghTabButtons) + gwinShow(ghPgButtons); + else if (tab == ghTabSliders) + gwinShow(ghPgSliders); + else if (tab == ghTabCheckboxes) + gwinShow(ghPgCheckboxes); + else if (tab == ghTabLabels) + gwinShow(ghPgLabels); + else if (tab == ghTabRadios) + gwinShow(ghPgRadios); + else if (tab == ghTabLists) + gwinShow(ghPgLists); + else if (tab == ghTabImages) + gwinShow(ghPgImages); + else if (tab == ghTabProgressbar) + gwinShow(ghPgProgressbars); + } +#endif + +// Wrap buttons onto the next line if they don't fit. static void setbtntext(GWidgetInit *pwi, coord_t maxwidth, char *txt) { if (pwi->g.x >= maxwidth) { pwi->g.x = 5; @@ -146,71 +191,101 @@ static void setbtntext(GWidgetInit *pwi, coord_t maxwidth, char *txt) { */ static void createWidgets(void) { GWidgetInit wi; - coord_t border; + coord_t border, pagewidth; gwinWidgetClearInit(&wi); - // Create the Tabs - wi.g.show = TRUE; wi.customDraw = gwinRadioDraw_Tab; - wi.g.height = TAB_HEIGHT; wi.g.y = 0; - wi.g.x = 0; setbtntext(&wi, ScrWidth, "Buttons"); - ghTabButtons = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Sliders"); - ghTabSliders = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Checkbox"); - ghTabCheckboxes = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Radios"); - ghTabRadios = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Lists"); - ghTabLists = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Labels"); - ghTabLabels = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Images"); - ghTabImages = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.x += wi.g.width; settabtext(&wi, "Progressbar"); - ghTabProgressbar = gwinRadioCreate(0, &wi, GROUP_TABS); - wi.g.y += wi.g.height; - wi.customDraw = 0; - // Calculate page borders based on screen size border = ScrWidth < 450 ? 1 : 5; - // Create the Pages - wi.g.show = FALSE; - wi.g.x = border; wi.g.y += border; - wi.g.width = ScrWidth/2 - border; wi.g.height = ScrHeight-wi.g.y-border; - ghPgButtons = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgSliders = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgCheckboxes = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgRadios = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgLists = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgLabels = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgImages = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - ghPgProgressbars = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); - wi.g.show = TRUE; + // Create the Tabs + #if GWIN_NEED_TABSET + wi.g.show = TRUE; + wi.g.x = border; wi.g.y = 0; + wi.g.width = ScrWidth - 2*border; wi.g.height = ScrHeight-wi.g.y-border; + ghTabset = gwinTabsetCreate(0, &wi, GWIN_TABSET_BORDER); + ghPgButtons = gwinTabsetAddTab(ghTabset, "Buttons", FALSE); + ghPgSliders = gwinTabsetAddTab(ghTabset, "Sliders", FALSE); + ghPgCheckboxes = gwinTabsetAddTab(ghTabset, "Checkbox", FALSE); + ghPgRadios = gwinTabsetAddTab(ghTabset, "Radios", FALSE); + ghPgLists = gwinTabsetAddTab(ghTabset, "Lists", FALSE); + ghPgLabels = gwinTabsetAddTab(ghTabset, "Labels", FALSE); + ghPgImages = gwinTabsetAddTab(ghTabset, "Images", FALSE); + ghPgProgressbars = gwinTabsetAddTab(ghTabset, "Progressbar", FALSE); - // Console - we apply some special colors before making it visible - wi.g.x = ScrWidth/2+border; - wi.g.width = ScrWidth/2 - 2*border; - ghConsole = gwinConsoleCreate(0, &wi.g); - gwinSetColor(ghConsole, Black); - gwinSetBgColor(ghConsole, HTML2COLOR(0xF0F0F0)); + pagewidth = gwinGetInnerWidth(ghTabset)/2; + + // Console - we apply some special colors before making it visible + // We put the console on the tabset itself rather than a tab-page. + // This makes it appear on every page :) + wi.g.parent = ghTabset; + wi.g.x = pagewidth; + wi.g.width = pagewidth; + ghConsole = gwinConsoleCreate(0, &wi.g); + gwinSetColor(ghConsole, Black); + gwinSetBgColor(ghConsole, HTML2COLOR(0xF0F0F0)); + + #else + wi.g.show = TRUE; wi.customDraw = gwinRadioDraw_Tab; + wi.g.height = TAB_HEIGHT; wi.g.y = 0; + wi.g.x = 0; setbtntext(&wi, ScrWidth, "Buttons"); + ghTabButtons = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Sliders"); + ghTabSliders = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Checkbox"); + ghTabCheckboxes = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Radios"); + ghTabRadios = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Lists"); + ghTabLists = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Labels"); + ghTabLabels = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Images"); + ghTabImages = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.x += wi.g.width; settabtext(&wi, "Progressbar"); + ghTabProgressbar = gwinRadioCreate(0, &wi, GROUP_TABS); + wi.g.y += wi.g.height; + wi.customDraw = 0; + + // Create the Pages + wi.g.show = FALSE; + wi.g.x = border; wi.g.y += border; + wi.g.width = ScrWidth/2 - border; wi.g.height = ScrHeight-wi.g.y-border; + ghPgButtons = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgSliders = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgCheckboxes = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgRadios = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgLists = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgLabels = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgImages = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + ghPgProgressbars = gwinContainerCreate(0, &wi, GWIN_CONTAINER_BORDER); + wi.g.show = TRUE; + + // Console - we apply some special colors before making it visible + wi.g.x = ScrWidth/2+border; + wi.g.width = ScrWidth/2 - 2*border; + ghConsole = gwinConsoleCreate(0, &wi.g); + gwinSetColor(ghConsole, Black); + gwinSetBgColor(ghConsole, HTML2COLOR(0xF0F0F0)); + + pagewidth = gwinGetInnerWidth(ghPgButtons); + #endif // Buttons wi.g.parent = ghPgButtons; wi.g.width = BUTTON_WIDTH; wi.g.height = BUTTON_HEIGHT; wi.g.y = 5; - wi.g.x = 5; setbtntext(&wi, gwinGetInnerWidth(ghPgButtons), "Button 1"); + wi.g.x = 5; setbtntext(&wi, pagewidth, "Button 1"); ghButton1 = gwinButtonCreate(0, &wi); - wi.g.x += wi.g.width+3; setbtntext(&wi, gwinGetInnerWidth(ghPgButtons), "Button 2"); + wi.g.x += wi.g.width+3; setbtntext(&wi, pagewidth, "Button 2"); ghButton2 = gwinButtonCreate(0, &wi); - wi.g.x += wi.g.width+3; setbtntext(&wi, gwinGetInnerWidth(ghPgButtons), "Button 3"); + wi.g.x += wi.g.width+3; setbtntext(&wi, pagewidth, "Button 3"); ghButton3 = gwinButtonCreate(0, &wi); - wi.g.x += wi.g.width+3; setbtntext(&wi, gwinGetInnerWidth(ghPgButtons), "Button 4"); + wi.g.x += wi.g.width+3; setbtntext(&wi, pagewidth, "Button 4"); ghButton4 = gwinButtonCreate(0, &wi); // Horizontal Sliders wi.g.parent = ghPgSliders; - wi.g.width = gwinGetInnerWidth(ghPgSliders) - 10; wi.g.height = SLIDER_WIDTH; + wi.g.width = pagewidth - 10; wi.g.height = SLIDER_WIDTH; wi.g.x = 5; wi.g.y = 5; wi.text = "S1"; ghSlider1 = gwinSliderCreate(0, &wi); gwinSliderSetPosition(ghSlider1, 33); @@ -242,7 +317,7 @@ static void createWidgets(void) { // Labels wi.g.parent = ghPgLabels; - wi.g.width = gwinGetInnerWidth(ghPgLabels)-10; wi.g.height = LABEL_HEIGHT; + wi.g.width = pagewidth-10; wi.g.height = LABEL_HEIGHT; wi.g.x = wi.g.y = 5; wi.text = "N/A"; ghLabelSlider1 = gwinLabelCreate(0, &wi); gwinLabelSetAttribute(ghLabelSlider1, 100, "Slider 1:"); @@ -265,20 +340,20 @@ static void createWidgets(void) { wi.g.width = RADIO_WIDTH; wi.g.height = RADIO_HEIGHT; wi.g.y = 5; wi.g.x = 5; wi.text = "Yes"; ghRadio1 = gwinRadioCreate(0, &wi, GROUP_YESNO); - wi.g.x += wi.g.width; wi.text = "No"; if (wi.g.x + wi.g.width > gwinGetInnerWidth(ghPgRadios)) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } + wi.g.x += wi.g.width; wi.text = "No"; if (wi.g.x + wi.g.width > pagewidth) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } ghRadio2 = gwinRadioCreate(0, &wi, GROUP_YESNO); gwinRadioPress(ghRadio1); wi.g.width = COLOR_WIDTH; wi.g.y += RADIO_HEIGHT+5; wi.g.x = 5; wi.text = "Black"; ghRadioBlack = gwinRadioCreate(0, &wi, GROUP_COLORS); - wi.g.x += wi.g.width; wi.text = "White"; if (wi.g.x + wi.g.width > gwinGetInnerWidth(ghPgRadios)) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } + wi.g.x += wi.g.width; wi.text = "White"; if (wi.g.x + wi.g.width > pagewidth) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } ghRadioWhite = gwinRadioCreate(0, &wi, GROUP_COLORS); - wi.g.x += wi.g.width; wi.text = "Yellow"; if (wi.g.x + wi.g.width > gwinGetInnerWidth(ghPgRadios)) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } + wi.g.x += wi.g.width; wi.text = "Yellow"; if (wi.g.x + wi.g.width > pagewidth) { wi.g.x = 5; wi.g.y += RADIO_HEIGHT; } ghRadioYellow = gwinRadioCreate(0, &wi, GROUP_COLORS); gwinRadioPress(ghRadioWhite); // Lists - border = gwinGetInnerWidth(ghPgLists) < 10+2*LIST_WIDTH ? 2 : 5; + border = pagewidth < 10+2*LIST_WIDTH ? 2 : 5; wi.g.parent = ghPgLists; wi.g.width = LIST_WIDTH; wi.g.height = LIST_HEIGHT; wi.g.y = border; wi.g.x = border; wi.text = "L1"; @@ -297,7 +372,7 @@ static void createWidgets(void) { gwinListAddItem(ghList1, "Item 11", FALSE); gwinListAddItem(ghList1, "Item 12", FALSE); gwinListAddItem(ghList1, "Item 13", FALSE); - wi.text = "L2"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > gwinGetInnerWidth(ghPgLists)) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } + wi.text = "L2"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > pagewidth) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } ghList2 = gwinListCreate(0, &wi, TRUE); gwinListAddItem(ghList2, "Item 0", FALSE); gwinListAddItem(ghList2, "Item 1", FALSE); @@ -313,7 +388,7 @@ static void createWidgets(void) { gwinListAddItem(ghList2, "Item 11", FALSE); gwinListAddItem(ghList2, "Item 12", FALSE); gwinListAddItem(ghList2, "Item 13", FALSE); - wi.text = "L3"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > gwinGetInnerWidth(ghPgLists)) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } + wi.text = "L3"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > pagewidth) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } ghList3 = gwinListCreate(0, &wi, TRUE); gwinListAddItem(ghList3, "Item 0", FALSE); gwinListAddItem(ghList3, "Item 1", FALSE); @@ -322,7 +397,7 @@ static void createWidgets(void) { gdispImageOpenFile(&imgYesNo, "image_yesno.gif"); gwinListItemSetImage(ghList3, 1, &imgYesNo); gwinListItemSetImage(ghList3, 3, &imgYesNo); - wi.text = "L4"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > gwinGetInnerWidth(ghPgLists)) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } + wi.text = "L4"; wi.g.x += LIST_WIDTH+border; if (wi.g.x + LIST_WIDTH > pagewidth) { wi.g.x = border; wi.g.y += LIST_HEIGHT+border; } ghList4 = gwinListCreate(0, &wi, TRUE); gwinListAddItem(ghList4, "Item 0", FALSE); gwinListAddItem(ghList4, "Item 1", FALSE); @@ -342,56 +417,50 @@ static void createWidgets(void) { // Image wi.g.parent = ghPgImages; - wi.g.x = wi.g.y = 0; wi.g.width = gwinGetInnerWidth(ghPgImages); wi.g.height = gwinGetInnerHeight(ghPgImages); + wi.g.x = wi.g.y = 0; wi.g.width = pagewidth; wi.g.height = gwinGetInnerHeight(ghPgImages); ghImage1 = gwinImageCreate(0, &wi.g); gwinImageOpenFile(ghImage1, "romfs_img_ugfx.gif"); // Progressbar wi.g.parent = ghPgProgressbars; - wi.g.width = gwinGetInnerWidth(ghPgImages)-10; wi.g.height = SLIDER_WIDTH; wi.g.y = 5; + wi.g.width = pagewidth-10; wi.g.height = SLIDER_WIDTH; wi.g.y = 5; wi.g.x = 5; wi.text = "Progressbar 1"; ghProgressbar1 = gwinProgressbarCreate(0, &wi); gwinProgressbarSetResolution(ghProgressbar1, 10); } /** - * Set the visibility of widgets based on which tab is selected. + * Set the value of the labels */ -static void setTab(GHandle tab) { - /* Make sure everything is invisible first */ - gwinHide(ghPgButtons); - gwinHide(ghPgSliders); - gwinHide(ghPgCheckboxes); - gwinHide(ghPgLabels); - gwinHide(ghPgRadios); - gwinHide(ghPgLists); - gwinHide(ghPgImages); - gwinHide(ghPgProgressbars); +static void setLabels(void) { + char tmp[20]; - // Stop the progress bar - gwinProgressbarStop(ghProgressbar1); - gwinProgressbarReset(ghProgressbar1); + // The sliders + snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider1)); + gwinSetText(ghLabelSlider1, tmp, TRUE); + snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider2)); + gwinSetText(ghLabelSlider2, tmp, TRUE); + snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider3)); + gwinSetText(ghLabelSlider3, tmp, TRUE); + snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider4)); + gwinSetText(ghLabelSlider4, tmp, TRUE); - /* Turn on widgets depending on the tab selected */ - if (tab == ghTabButtons) { - gwinShow(ghPgButtons); - } else if (tab == ghTabSliders) { - gwinShow(ghPgSliders); - } else if (tab == ghTabCheckboxes) { - gwinShow(ghPgCheckboxes); - } else if (tab == ghTabLabels) { - gwinShow(ghPgLabels); - } else if (tab == ghTabRadios) { - gwinShow(ghPgRadios); - } else if (tab == ghTabLists) { - gwinShow(ghPgLists); - } else if (tab == ghTabImages) { - gwinShow(ghPgImages); - } else if (tab == ghTabProgressbar) { - gwinShow(ghPgProgressbars); + // The radio buttons + if (gwinRadioIsPressed(ghRadio1)) + gwinSetText(ghLabelRadio1, "Yes", TRUE); + else if (gwinRadioIsPressed(ghRadio2)) + gwinSetText(ghLabelRadio1, "No", TRUE); +} - // Start the progress bar +/** + * Control the progress bar auto-increment + */ +static void setProgressbar(bool_t onoff) { + if (onoff) gwinProgressbarStart(ghProgressbar1, 500); + else { + gwinProgressbarStop(ghProgressbar1); // Stop the progress bar + gwinProgressbarReset(ghProgressbar1); } } @@ -445,8 +514,10 @@ int main(void) { geventListenerInit(&gl); gwinAttachListener(&gl); - // Press the Tab we want visible - gwinRadioPress(ghTabButtons); + #if !GWIN_NEED_TABSET + // Press the Tab we want visible + gwinRadioPress(ghTabButtons); + #endif while(1) { // Get an Event @@ -480,33 +551,18 @@ int main(void) { gwinPrintf(ghConsole, "Radio Group %u=%s\n", ((GEventGWinRadio *)pe)->group, gwinGetText(((GEventGWinRadio *)pe)->gwin)); switch(((GEventGWinRadio *)pe)->group) { - case GROUP_TABS: + #if !GWIN_NEED_TABSET + case GROUP_TABS: - // Set control visibility depending on the tab selected - setTab(((GEventGWinRadio *)pe)->gwin); + // Set control visibility depending on the tab selected + setTab(((GEventGWinRadio *)pe)->gwin); - // We show the state of some of the GUI elements here - if (((GEventGWinRadio *)pe)->gwin == ghTabLabels) { - char tmp[20]; - - // The sliders - snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider1)); - gwinSetText(ghLabelSlider1, tmp, TRUE); - snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider2)); - gwinSetText(ghLabelSlider2, tmp, TRUE); - snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider3)); - gwinSetText(ghLabelSlider3, tmp, TRUE); - snprintg(tmp, sizeof(tmp), "%d%%", gwinSliderGetPosition(ghSlider4)); - gwinSetText(ghLabelSlider4, tmp, TRUE); - - // The radio buttons - if (gwinRadioIsPressed(ghRadio1)) { - gwinSetText(ghLabelRadio1, "Yes", TRUE); - } else if (gwinRadioIsPressed(ghRadio2)) { - gwinSetText(ghLabelRadio1, "No", TRUE); - } - } - break; + // We show the state of some of the GUI elements here + setProgressbar(((GEventGWinRadio *)pe)->gwin == ghTabProgressbar); + if (((GEventGWinRadio *)pe)->gwin == ghTabLabels) + setLabels(); + break; + #endif case GROUP_COLORS: { @@ -531,6 +587,17 @@ int main(void) { } break; + #if GWIN_NEED_TABSET + case GEVENT_GWIN_TABSET: + gwinPrintf(ghConsole, "TabPage %u (%s)\n", ((GEventGWinTabset *)pe)->nPage, gwinTabsetGetTitle(((GEventGWinTabset *)pe)->ghPage)); + + // We show the state of some of the GUI elements here + setProgressbar(((GEventGWinTabset *)pe)->ghPage == ghPgProgressbars); + if (((GEventGWinTabset *)pe)->ghPage == ghPgLabels) + setLabels(); + break; + #endif + default: gwinPrintf(ghConsole, "Unknown %d\n", pe->type); break; diff --git a/gfxconf.example.h b/gfxconf.example.h index ab56ff90..003cb4cf 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -179,6 +179,8 @@ //#define GWIN_NEED_CONTAINERS FALSE // #define GWIN_NEED_CONTAINER FALSE // #define GWIN_NEED_FRAME FALSE +// #define GWIN_NEED_TABSET FALSE +// #define GWIN_TABSET_TABHEIGHT 18 /////////////////////////////////////////////////////////////////////////// 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."