Menu

From WikiPrizm
Jump to: navigation, search

Synopsis

This is a routine that displays a menu system similar to Geometry plugin's one.


Definition

Inputs

int menu(struct Tmenu* mymenu, unsigned char selected_page, unsigned char selected_tab)

  • struct Tmenu* mymenu - the structure, which contains the menu system (pages, tabs and items)
  • unsigned char selected_page - the function draws this page
  • unsigned char selected_tab - the function draws this tab of the selected page

Output

The function returns the ID of the selected menu item as integer, or -1 if [EXIT] was pressed

Data structure

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[];
};

Configuration

The following constants can manipulate the style and the sizes of the menu. The meanings of these constants are visible on s1.png in the downloadable package

#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 displayed.

Source

Full source code with example and documentation is downloadable from cemetech achieves.

menu.hpp

//Created by Balping
//http://www.cemetech.net

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))

void CopySpriteMasked2bitR(const unsigned char* data, int x, int y, int width, int height, short unsigned int* palette, unsigned char mask){
	short unsigned int* VRAM = (short unsigned int*)0xA8000000;
	VRAM += (LCD_WIDTH_PX*y + x);
	int offset = 0;
	unsigned char buf;
	unsigned char this;
	int availbits = 0;
	for(int j=y; j<y+height; j++) {
		for(int i=x; i<x+width;  i++) {
			if (!availbits) {
				buf = data[offset++];
				availbits = 8;
			}
			this = ((buf&0xC0)>>6);
			if (this != mask){
				*VRAM = palette[(int)this];
			}
			VRAM++;
			buf<<=2;
			availbits-=2;
		}
		VRAM += (LCD_WIDTH_PX-width);
	}
}

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;
}

Example

Example initialization of a menu object (this is the copy of the Geometry plugin's menu system):

//                     v-----The tab names are not important                  v----ID of the menu item, it's returned on select                 v----Menu text
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"}}};
//      The label of the tag----^        ^----Numbers of the items on this tab. Cannot be grater than 9
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"}}};

//                      v-----The page names are not important    v-------pointer to the previously defined tab variable
static struct menu_page Page0 = {"OPT", KEY_CTRL_OPTN, 2, {&Tab0, &Tab1}};
//Displayed in the upper left corner^   ^----the program automatically jumps here if this key is pressed
static struct menu_page Page1 = {"F1", KEY_CTRL_F1, 2, {&Tab2, &Tab3}};
//                 Number of tabs on this page------^
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}};

//      Number of the pages-----v   v-------pointer to the previously defined page variable
static struct Tmenu geometry = {7, {&Page0,&Page1,&Page2,&Page3,&Page4,&Page5,&Page6}};
//   ^-----every struct must be static

//How to call the function:
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