static void MenuDisplaySubmenus(const MenuSystem *ms) { int x = 0, yStart = 0; const menu_t *menu = ms->current; switch (menu->type) { // TODO: refactor the three menu types (normal, options, campaign) into one case MENU_TYPE_NORMAL: case MENU_TYPE_OPTIONS: { int iStart = 0; int iEnd = (int)menu->u.normal.subMenus.size; int numMenuLines = 0; int maxIEnd = (int)menu->u.normal.subMenus.size; if (menu->u.normal.maxItems > 0) { // Calculate first/last indices if (menu->u.normal.scroll != 0) { iStart = menu->u.normal.scroll; } maxIEnd = iStart + menu->u.normal.maxItems; } // Count the number of menu items that can fit // This is to account for multi-line items for (iEnd = iStart; iEnd < maxIEnd && iEnd < (int)menu->u.normal.subMenus.size; iEnd++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, iEnd); const int numLines = FontStrNumLines(subMenu->name); if (menu->u.normal.maxItems > 0 && numMenuLines + numLines > menu->u.normal.maxItems) { break; } numMenuLines += numLines; } int maxWidth = 0; for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const int width = FontStrW(subMenu->name); if (width > maxWidth) { maxWidth = width; } } // Limit max width if it is larger than the menu system size maxWidth = MIN(ms->size.x, maxWidth); const bool isCentered = menu->type == MENU_TYPE_NORMAL; switch (ms->align) { case MENU_ALIGN_CENTER: x = MS_CENTER_X(*ms, maxWidth); if (!isCentered) { x -= 20; } break; case MENU_ALIGN_LEFT: x = ms->pos.x; break; default: assert(0 && "unknown alignment"); break; } yStart = MS_CENTER_Y(*ms, numMenuLines * FontH()); if (menu->u.normal.maxItems > 0) { // Display scroll arrows if (menu->u.normal.scroll != 0) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('^')), yStart - 2 - FontH()), "^", 0, 0, colorBlack); } if (iEnd < (int)menu->u.normal.subMenus.size - 1) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('v')), yStart + numMenuLines*FontH() + 2), "v", 0, 0, colorBlack); } } const int xOptions = x + maxWidth + 10; // Display normal menu items Vec2i pos = Vec2iNew(x, yStart); for (int i = iStart; i < iEnd; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); char *nameBuf; CMALLOC(nameBuf, strlen(subMenu->name) + 3); if (subMenu->type == MENU_TYPE_NORMAL && subMenu->u.normal.isSubmenusAlt) { sprintf(nameBuf, "%s >", subMenu->name); } else { strcpy(nameBuf, subMenu->name); } switch (menu->u.normal.align) { case MENU_ALIGN_CENTER: pos.x = MS_CENTER_X(*ms, FontStrW(nameBuf)); break; case MENU_ALIGN_LEFT: // Do nothing break; default: assert(0 && "unknown alignment"); break; } const int yNext = DisplayMenuItem( pos, nameBuf, i == menu->u.normal.index, subMenu->isDisabled, subMenu->color).y + FontH(); // display option value if (subMenu->type == MENU_TYPE_SET_OPTION_TOGGLE || subMenu->type == MENU_TYPE_SET_OPTION_RANGE || subMenu->type == MENU_TYPE_SET_OPTION_SEED || subMenu->type == MENU_TYPE_SET_OPTION_UP_DOWN_VOID_FUNC_VOID || subMenu->type == MENU_TYPE_SET_OPTION_RANGE_GET_SET) { const int optionInt = MenuOptionGetIntValue(subMenu); const Vec2i value_pos = Vec2iNew(xOptions, pos.y); switch (subMenu->u.option.displayStyle) { case MENU_OPTION_DISPLAY_STYLE_INT: { char buf[32]; sprintf(buf, "%d", optionInt); FontStr(buf, value_pos); } break; case MENU_OPTION_DISPLAY_STYLE_YES_NO: FontStr(optionInt ? "Yes" : "No", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_ON_OFF: FontStr(optionInt ? "On" : "Off", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_STR_FUNC: FontStr(subMenu->u.option.uFunc.str(), value_pos); break; case MENU_OPTION_DISPLAY_STYLE_INT_TO_STR_FUNC: FontStr( subMenu->u.option.uFunc.intToStr(optionInt), value_pos); break; default: break; } } pos.y = yNext; } } break; case MENU_TYPE_KEYS: { int xKeys; x = MS_CENTER_X(*ms, (FontW('a') * 10)) / 2; xKeys = x * 3; yStart = (gGraphicsDevice.cachedConfig.Res.y / 2) - (FontH() * 10); for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { int y = yStart + i * FontH(); int isSelected = i == menu->u.normal.index; const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const char *name = subMenu->name; if (isSelected && subMenu->type != MENU_TYPE_SET_OPTION_CHANGE_KEY) { FontStrMask(name, Vec2iNew(x, y), colorRed); } else { FontStr(name, Vec2iNew(x, y)); } if (subMenu->type == MENU_TYPE_SET_OPTION_CHANGE_KEY) { const char *keyName; if (menu->u.normal.changeKeyMenu == subMenu) { keyName = "Press a key"; } else if (subMenu->u.changeKey.code == KEY_CODE_MAP) { keyName = SDL_GetKeyName(gConfig.Input.PlayerKeys[0].Keys.map); } else { keyName = SDL_GetKeyName(InputGetKey( subMenu->u.changeKey.keys, subMenu->u.changeKey.code)); } DisplayMenuItem( Vec2iNew(xKeys, y), keyName, isSelected, 0, colorBlack); } } } break; default: // No submenus, don't display anything break; } }
void MenuDisplaySubmenus(MenuSystem *ms) { int i; int x = 0, yStart = 0; int maxWidth = 0; menu_t *menu = ms->current; switch (menu->type) { // TODO: refactor the three menu types (normal, options, campaign) into one case MENU_TYPE_NORMAL: case MENU_TYPE_OPTIONS: { int isCentered = menu->type == MENU_TYPE_NORMAL; int xOptions; for (i = 0; i < menu->u.normal.numSubMenus; i++) { int width = TextGetStringWidth(menu->u.normal.subMenus[i].name); if (width > maxWidth) { maxWidth = width; } } switch (ms->align) { case MENU_ALIGN_CENTER: x = MS_CENTER_X(*ms, maxWidth); if (!isCentered) { x -= 20; } break; case MENU_ALIGN_LEFT: x = ms->pos.x; break; default: assert(0 && "unknown alignment"); break; } yStart = MS_CENTER_Y( *ms, menu->u.normal.numSubMenus * CDogsTextHeight()); xOptions = x + maxWidth + 10; // Display normal menu items for (i = 0; i < menu->u.normal.numSubMenus; i++) { int y = yStart + i * CDogsTextHeight(); menu_t *subMenu = &menu->u.normal.subMenus[i]; // Display menu item const char *name = subMenu->name; if (i == menu->u.normal.index) { DrawTextStringMasked(name, ms->graphics, Vec2iNew(x, y), colorRed); } else if (subMenu->isDisabled) { color_t dark = { 64, 64, 64, 255 }; DrawTextStringMasked(name, ms->graphics, Vec2iNew(x, y), dark); } else { DrawTextString(name, ms->graphics, Vec2iNew(x, y)); } // display option value if (subMenu->type == MENU_TYPE_SET_OPTION_TOGGLE || subMenu->type == MENU_TYPE_SET_OPTION_RANGE || subMenu->type == MENU_TYPE_SET_OPTION_SEED || subMenu->type == MENU_TYPE_SET_OPTION_UP_DOWN_VOID_FUNC_VOID || subMenu->type == MENU_TYPE_SET_OPTION_RANGE_GET_SET || subMenu->type == MENU_TYPE_VOID_FUNC_VOID) { int optionInt = MenuOptionGetIntValue(subMenu); switch (subMenu->u.option.displayStyle) { case MENU_OPTION_DISPLAY_STYLE_INT: CDogsTextIntAt(xOptions, y, optionInt); break; case MENU_OPTION_DISPLAY_STYLE_YES_NO: CDogsTextStringAt(xOptions, y, optionInt ? "Yes" : "No"); break; case MENU_OPTION_DISPLAY_STYLE_ON_OFF: CDogsTextStringAt(xOptions, y, optionInt ? "On" : "Off"); break; case MENU_OPTION_DISPLAY_STYLE_STR_FUNC: CDogsTextStringAt(xOptions, y, subMenu->u.option.uFunc.str()); break; case MENU_OPTION_DISPLAY_STYLE_INT_TO_STR_FUNC: CDogsTextStringAt(xOptions, y, subMenu->u.option.uFunc.intToStr(optionInt)); break; default: break; } } } } break; case MENU_TYPE_CAMPAIGNS: { int y = MS_CENTER_Y(*ms, 12 * CDogsTextHeight()); #define ARROW_UP "\036" #define ARROW_DOWN "\037" if (menu->u.normal.scroll != 0) { DisplayMenuItem( MS_CENTER_X(*ms, TextGetStringWidth(ARROW_UP)), y - 2 - CDogsTextHeight(), ARROW_UP, 0); } for (i = menu->u.normal.scroll; i < MIN(menu->u.normal.scroll + 12, menu->u.normal.numSubMenus); i++) { int isSelected = i == menu->u.normal.index; menu_t *subMenu = &menu->u.normal.subMenus[i]; const char *name = subMenu->name; // TODO: display subfolders DisplayMenuItem( MS_CENTER_X(*ms, TextGetStringWidth(name)), y, name, isSelected); if (isSelected && subMenu->type == MENU_TYPE_CAMPAIGN_ITEM) { char s[255]; const char *filename = subMenu->u.campaign.filename; int isBuiltin = subMenu->u.campaign.isBuiltin; sprintf(s, "( %s )", isBuiltin ? "Internal" : filename); DrawTextStringSpecial( s, TEXT_XCENTER | TEXT_BOTTOM, ms->pos, ms->size, Vec2iNew(ms->size.x / 12, 0)); } y += CDogsTextHeight(); } if (i < menu->u.normal.numSubMenus - 1) { DisplayMenuItem( MS_CENTER_X(*ms, TextGetStringWidth(ARROW_DOWN)), y + 2, ARROW_DOWN, 0); } } break; case MENU_TYPE_KEYS: { int xKeys; x = MS_CENTER_X(*ms, (CDogsTextCharWidth('a') * 10)) / 2; xKeys = x * 3; yStart = (gGraphicsDevice.cachedConfig.ResolutionHeight / 2) - (CDogsTextHeight() * 10); for (i = 0; i < menu->u.normal.numSubMenus; i++) { int y = yStart + i * CDogsTextHeight(); int isSelected = i == menu->u.normal.index; menu_t *subMenu = &menu->u.normal.subMenus[i]; const char *name = subMenu->name; if (isSelected && subMenu->type != MENU_TYPE_SET_OPTION_CHANGE_KEY) { CDogsTextStringWithTableAt(x, y, name, &tableFlamed); } else { CDogsTextStringAt(x, y, name); } if (subMenu->type == MENU_TYPE_SET_OPTION_CHANGE_KEY) { const char *keyName; if (menu->u.normal.changeKeyMenu == subMenu) { keyName = "Press a key"; } else if (subMenu->u.changeKey.code == KEY_CODE_MAP) { keyName = SDL_GetKeyName(gConfig.Input.PlayerKeys[0].Keys.map); } else { keyName = SDL_GetKeyName(InputGetKey( subMenu->u.changeKey.keys, subMenu->u.changeKey.code)); } DisplayMenuItem(xKeys, y, keyName, isSelected); } } } break; default: // No submenus, don't display anything break; } }