aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demos/modules/gwin/widgets/gfxconf.h2
-rw-r--r--demos/modules/gwin/widgets/main.c333
-rw-r--r--gfxconf.example.h2
-rw-r--r--src/gwin/gwin_container.h3
-rw-r--r--src/gwin/gwin_tabset.c562
-rw-r--r--src/gwin/gwin_tabset.h206
-rw-r--r--src/gwin/sys_make.mk1
-rw-r--r--src/gwin/sys_options.h14
-rw-r--r--src/gwin/sys_rules.h2
9 files changed, 991 insertions, 134 deletions
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;
+
+ /**
+ * 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 tabs onto the next line if they don't fit.
+// 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;
-
- // 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));
+ // 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);
+
+ 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);
-
- // Stop the progress bar
- gwinProgressbarStop(ghProgressbar1);
- gwinProgressbarReset(ghProgressbar1);
-
- /* 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);
-
- // Start the progress bar
+static void setLabels(void) {
+ 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);
+}
+
+/**
+ * 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.<br/>
+ * gwinTabsetDraw_Transparent() will not fill the client area at all.<br/>
+ * gwinTabsetDraw_Image() will tile the image throughout the client area.<br/>
+ * 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."