Here's my full featured menu-system routine. It requires some other functions and data structures. Full documentation available in my example
package. Description also available
here
To get every required function,
read the documentation. They are included in menu.hpp in the package
Data structures:
Code: struct menu_item{
int id;
char *text;
};
struct menu_tab{
char *label;
unsigned char item_count;
struct menu_item items[];
};
struct menu_page{
char *label;
int key;
unsigned char tab_count;
struct menu_tab *tabs[];
};
struct Tmenu{
unsigned char page_count;
struct menu_page *pages[];
};
Config:
Code: #define MENU_X 6
#define MENU_Y 24
#define MENU_HEIGHT 191
#define MENU_WIDTH 326
#define MENU_HEADER_HEIGHT 27
#define MENU_TAB_INDENT 30
#define MENU_TAB_WIDTH 140
#define MENU_TAB_SPACE 4
#define MENU_COLOR COLOR_LIME
For example you can display more than 2 tabs on the same page, if you set a low value to MENU_TAB_WIDTH or a longer than 2 character long text in the upper left corner by changing MENU_TAB_INDENT. If its needed, you can transform this constants to function arguments with the same name.
If a label is longer than it's space then the rest of the string is not shown.
Function:
Code: int menu(struct Tmenu *mymenu, unsigned char selected_page, unsigned char selected_tab){
const color_t tab_palette[4] = {MENU_COLOR, COLOR_BLACK, COLOR_WHITE, MENU_COLOR};
const unsigned char left_corner[18] = {1, 90, 129, 107, 241, 111, 252, 111, 255, 91, 255, 219, 255, 214, 255, 213, 191, 213};
const unsigned char right_corner[18] = {169, 84, 63, 165, 79, 254, 83, 255, 229, 255, 249, 95, 255, 85, 255, 213, 111, 245};
unsigned char go=1, page, tab, i, starti, selected_item, mehet, item_count;
unsigned short tabplus=0;
int x, y, lwidth, key, retval=0;
unsigned char cheight = (MENU_HEIGHT-MENU_HEADER_HEIGHT-18)/24;
unsigned char cwidth = (MENU_WIDTH-16)/18;
char buff[cwidth+1];
buff[1] = ':'; buff[cwidth] = 0;
SaveVRAM_1();
do{
fillArea(MENU_X, MENU_Y, 1, MENU_HEIGHT, COLOR_BLACK);
fillArea(MENU_X+1, MENU_Y+MENU_HEADER_HEIGHT+1, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-3, COLOR_WHITE);
fillArea(MENU_X+1, MENU_Y, MENU_TAB_INDENT+4, 1, COLOR_BLACK);
fillArea(MENU_X, MENU_Y+MENU_HEIGHT-2, MENU_WIDTH, 2, COLOR_BLACK);
fillArea(MENU_X+MENU_WIDTH-2, MENU_Y+MENU_HEADER_HEIGHT+1, 2, MENU_HEIGHT-MENU_HEADER_HEIGHT-3, COLOR_BLACK);
fillArea(MENU_X, MENU_Y+MENU_HEADER_HEIGHT, MENU_WIDTH, 1, COLOR_BLACK);
fillArea(MENU_X+1, MENU_Y+MENU_HEADER_HEIGHT+1, MENU_WIDTH-3, 1, COLOR_WHITE);
fillArea(MENU_X+2, MENU_Y+MENU_HEADER_HEIGHT+2, MENU_WIDTH-4, 4, MENU_COLOR);
fillArea(MENU_X+2, MENU_Y+MENU_HEIGHT-6, MENU_WIDTH-4, 4, MENU_COLOR);
fillArea(MENU_X+2, MENU_Y+MENU_HEADER_HEIGHT+6, 4, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, MENU_COLOR);
fillArea(MENU_WIDTH-MENU_X+6, MENU_Y+MENU_HEADER_HEIGHT+6, 4, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, MENU_COLOR);
fillArea(MENU_X+6, MENU_Y+MENU_HEADER_HEIGHT+6, 2, MENU_HEIGHT-MENU_HEADER_HEIGHT-13, COLOR_BLACK);
fillArea(MENU_WIDTH-MENU_X+4, MENU_Y+MENU_HEADER_HEIGHT+6, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-13, COLOR_BLACK);
fillArea(MENU_WIDTH-MENU_X+5, MENU_Y+MENU_HEADER_HEIGHT+6, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, COLOR_WHITE);
fillArea(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT+6, MENU_WIDTH-16, 2, COLOR_BLACK);
fillArea(MENU_X+8, MENU_Y+MENU_HEIGHT-8, MENU_WIDTH-16, 1, COLOR_BLACK);
fillArea(MENU_X+6, MENU_Y+MENU_HEIGHT-7, MENU_WIDTH-13, 1, COLOR_WHITE);
tabplus=0;
for(tabplus=0; tabplus<mymenu->pages[selected_page]->tab_count*(MENU_TAB_WIDTH+MENU_TAB_SPACE); tabplus+=MENU_TAB_WIDTH+MENU_TAB_SPACE){
CopySpriteMasked2bitR(left_corner, tabplus+MENU_X+MENU_TAB_INDENT, MENU_Y+1, 9, 8, tab_palette, 5*!tabplus);
CopySpriteMasked2bitR(right_corner, tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-9, MENU_Y+1, 9, 8, tab_palette, 0);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+1, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+2, MENU_Y+9, 4, MENU_HEADER_HEIGHT-9, MENU_COLOR);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+6, MENU_Y+9, 2, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-8, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-7, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-6, MENU_Y+9, 4, MENU_HEADER_HEIGHT-9, MENU_COLOR);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-2, MENU_Y+9, 2, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+6, MENU_TAB_WIDTH-18, 2, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+2, MENU_TAB_WIDTH-18, 4, MENU_COLOR);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+1, MENU_TAB_WIDTH-18, 1, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+8, MENU_TAB_WIDTH-18, 1, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+5, MENU_Y, MENU_TAB_WIDTH-9, 1, COLOR_BLACK);
}
tabplus=selected_tab*(MENU_TAB_WIDTH+MENU_TAB_SPACE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+8, MENU_Y+MENU_HEADER_HEIGHT, MENU_TAB_WIDTH-16, 8, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+6, MENU_Y+MENU_HEADER_HEIGHT+1, 2, 5, COLOR_BLACK);
plot(tabplus+MENU_X+MENU_TAB_INDENT+1, MENU_Y+MENU_HEADER_HEIGHT, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+2, MENU_Y+MENU_HEADER_HEIGHT, 4, 2, MENU_COLOR);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-8, MENU_Y+MENU_HEADER_HEIGHT+1, 1, 5, COLOR_BLACK);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-7, MENU_Y+MENU_HEADER_HEIGHT, 1, 6, COLOR_WHITE);
fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-6, MENU_Y+MENU_HEADER_HEIGHT, 4, 2, MENU_COLOR);
y=(MENU_HEADER_HEIGHT-27)/2 + 10;
for(tab=0; tab<mymenu->pages[selected_page]->tab_count; tab++){
x=MENU_X+MENU_TAB_INDENT+tab*(MENU_TAB_WIDTH+MENU_TAB_SPACE)+11;
fillArea(x-3, MENU_Y+9, MENU_TAB_WIDTH-16, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
PrintMini(&x, &y, mymenu->pages[selected_page]->tabs[tab]->label, 0x02, x+MENU_TAB_WIDTH-24, 0,0,COLOR_BLACK, COLOR_WHITE, 1,0);
}
x=MENU_X+1; lwidth=x;
y = MENU_HEADER_HEIGHT/2-6;
PrintMini(&x, &y, mymenu->pages[selected_page]->label, 0, x+MENU_TAB_INDENT-1, 0,0,COLOR_BLACK, MENU_COLOR, 1,0);
lwidth = x-lwidth;
x = max(MENU_X + (MENU_TAB_INDENT-1-lwidth)/2 + 1, MENU_X+1);
fillArea(MENU_X+1, MENU_Y+1, MENU_TAB_INDENT-1, MENU_HEADER_HEIGHT-1, MENU_COLOR);
PrintMini(&x, &y, mymenu->pages[selected_page]->label, 0, x+MENU_TAB_INDENT-1, 0,0,COLOR_BLACK, MENU_COLOR, 1,0);
fillArea(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT+8, MENU_WIDTH-16, MENU_HEIGHT-MENU_HEADER_HEIGHT-16, COLOR_WHITE);
starti=0; selected_item=0;
mehet=1;
item_count = mymenu->pages[selected_page]->tabs[selected_tab]->item_count;
do{
for(i=starti; i<starti+cheight && i<item_count && i<9; i++){
buff[0] = '1' + i;
sys_memset(buff+2, ' ', cwidth-2);
memcpy(buff+2, mymenu->pages[selected_page]->tabs[selected_tab]->items[i].text,min(strlen(mymenu->pages[selected_page]->tabs[selected_tab]->items[i].text),cwidth-2));
PrintCXY(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT-15+(i-starti)*24, buff, i==selected_item, -1, COLOR_BLACK, COLOR_WHITE, 1, 0);
fillArea(MENU_X+8+cwidth*18, MENU_Y+MENU_HEADER_HEIGHT+9+(i-starti)*24, MENU_WIDTH-16-cwidth*18, 24, i==selected_item ? COLOR_BLACK : COLOR_WHITE);
}
if(starti>0){
PrintCXY(MENU_X+MENU_WIDTH-26, MENU_Y+MENU_HEADER_HEIGHT-15, "\xE6\x92", TEXT_MODE_NORMAL, -1, starti==selected_item?COLOR_LIME:COLOR_FUCHSIA, starti==selected_item?COLOR_BLACK:COLOR_WHITE, 1, 0);
}
if(item_count>starti+cheight){
PrintCXY(MENU_X+MENU_WIDTH-26, MENU_Y+MENU_HEADER_HEIGHT-15+(cheight-1)*24, "\xE6\x93", TEXT_MODE_NORMAL, -1, starti+cheight-1==selected_item?COLOR_LIME:COLOR_FUCHSIA, starti+cheight-1==selected_item?COLOR_BLACK:COLOR_WHITE, 1, 0);
}
GetKey(&key);
switch(key){
case KEY_CTRL_UP:
selected_item = (selected_item+item_count-1)%item_count;
break;
case KEY_CTRL_DOWN:
selected_item = (selected_item+1)%item_count;
break;
case KEY_CHAR_1:
case KEY_CHAR_2:
case KEY_CHAR_3:
case KEY_CHAR_4:
case KEY_CHAR_5:
case KEY_CHAR_6:
case KEY_CHAR_7:
case KEY_CHAR_8:
case KEY_CHAR_9:
selected_item = min(key-KEY_CHAR_1, item_count-1);
break;
default:
mehet=0;
break;
}
if(starti > selected_item){starti=selected_item;}
if(starti+cheight <= selected_item){starti=selected_item-cheight+1;}
} while(mehet);
switch(key){
case KEY_CTRL_RIGHT:
if(mymenu->pages[selected_page]->tab_count-1 == selected_tab){
selected_page = (selected_page+1)%mymenu->page_count;
selected_tab = 0;
}else{
selected_tab++;
}
break;
case KEY_CTRL_LEFT:
if(selected_tab == 0){
selected_page = (selected_page+mymenu->page_count-1)%mymenu->page_count;
selected_tab = mymenu->pages[selected_page]->tab_count-1;
}else{
selected_tab--;
}
break;
case KEY_CTRL_EXIT:
go=0;
retval=-1;
break;
case KEY_CTRL_EXE:
go=0;
retval = mymenu->pages[selected_page]->tabs[selected_tab]->items[selected_item].id;
default:
for(page=0; page<mymenu->page_count; page++){
if(mymenu->pages[page]->key == key){
selected_page = page;
selected_tab = 0;
}
}
break;
}
LoadVRAM_1();
} while(go);
return retval;
}
Params:
int menu(struct Tmenu *mymenu, unsigned char selected_page, unsigned char selected_tab)
mymenu: the structure, which contains the menu system (pages, tabs and items)
selected_page: the function draws this page
selected_tab: the function draws this tab of the selected page
return value: returns the ID of the selected menu item, or -1 if [EXIT] was pressed
Example initialization of a menu object (this is the copy of the Geometry plugin's menu system):
Code: static struct menu_tab Tab0 = {"Option", 7, {{1, "Text"}, {2, "Expression"}, {3, "Number Format"}, {4, "Clr Constraint"}, {5, "Show All"}, {6, "Hide"}, {7, "Area Calc"}}};
static struct menu_tab Tab1 = {"Properties", 5, {{11, "to the front"}, {12, "to the back"}, {13, "All Text"}, {14, "Fade I/O"}, {15, "Store Picture"}}};
static struct menu_tab Tab2 = {"File", 4, {{21, "New"}, {22, "Open"}, {23, "Save as"}, {24, "Key Help"}}};
static struct menu_tab Tab3 = {"View", 6, {{31, "Zoom Box"}, {32, "Pan"}, {33, "Scroll"}, {34, "Zoom In"}, {35, "Zoom Out"}, {36, "Zoom to Fit"}}};
static struct menu_tab Tab4 = {"Edit", 6, {{41, "Undo/Redo"}, {42, "Select All"}, {43, "Deselect All"}, {44, "Select Figure"}, {45, "Delete"}, {46, "Clear All"}}};
static struct menu_tab Tab5 = {"Draw", 8, {{51, "Point"}, {52, "Line Segment"}, {53, "Infinite Line"}, {54, "Ray"}, {55, "Vector"}, {56, "Circle"}, {57, "Arc"}, {58, "SemiCirc(Diam)"}}};
static struct menu_tab Tab6 = {"Draw Spec", 7, {{61, "Triangle"}, {62, "Isocs Triangle"}, {63, "Rectangle"}, {64, "Square"}, {65, "Polygon"}, {66, "Regular n-gon"}, {67, "Function f(x)"}}};
static struct menu_tab Tab7 = {"Construct", 8, {{71, "Perp Bisector"}, {72, "Perpendicular"}, {73, "Midpoint"}, {74, "Intersection"}, {75, "Angle Bisector"}, {76, "Parallel"}, {77, "Tangent"}, {78, "Attached Angle"}}};
static struct menu_tab Tab8 = {"Transform", 6, {{81, "Reflection"}, {82, "Translation"}, {83, "Trans(Sel Vec)"}, {84, "Rotation"}, {85, "Dilation"}, {86, "Symmetry"}}};
static struct menu_tab Tab9 = {"Animate", 8, {{91, "Add Animation"}, {92, "Replace Anima"}, {93, "Trace"}, {94, "Edit Animation"}, {95, "Go(once)"}, {96, "Go(repeat)"}, {97, "Add Table"}, {98, "Display Table"}}};
static struct menu_page Page0 = {"OPT", KEY_CTRL_OPTN, 2, {&Tab0, &Tab1}};
static struct menu_page Page1 = {"F1", KEY_CTRL_F1, 2, {&Tab2, &Tab3}};
static struct menu_page Page2 = {"F2", KEY_CTRL_F2, 1, {&Tab4}};
static struct menu_page Page3 = {"F3", KEY_CTRL_F3, 2, {&Tab5, &Tab6}};
static struct menu_page Page4 = {"F4", KEY_CTRL_F4, 1, {&Tab7}};
static struct menu_page Page5 = {"F5", KEY_CTRL_F5, 1, {&Tab8}};
static struct menu_page Page6 = {"F6", KEY_CTRL_F6, 1, {&Tab9}};
static struct Tmenu geometry = {7, {&Page0,&Page1,&Page2,&Page3,&Page4,&Page5,&Page6}};
int choice = menu(&geometry, 3, 1);
Controls:
Press keys UP and DOWN to select items
Press keys LEFT and RIGHT to jump to the next or the previous tab
Press EXE to select an item
Press EXIT to close the menu without selecting an item
Press your pre-defined keys to jump to page
Notices:
This function uses SaveVRAM_1(). So your previously saved VRAM data will be overwritten.
This function uses CopySpriteMasked2bitR() which is different from CopySpriteMasked2bit() and CopySpriteMaskedNbit()
This function uses sys_memset() and PrintCXY(). They might not be in your libfxcg. You can download the newest version from here: https://github.com/Jonimoose/libfxcg