852 lines
27 KiB
C++
Executable File
852 lines
27 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "console/consoleTypes.h"
|
|
#include "console/console.h"
|
|
#include "dgl/dgl.h"
|
|
#include "gui/core/guiCanvas.h"
|
|
#include "gui/core/guiDefaultControlRender.h"
|
|
#include "gui/controls/guiTextListCtrl.h"
|
|
#include "sim/actionMap.h"
|
|
#include "gui/editor/guiMenuBar.h"
|
|
|
|
// menu bar:
|
|
// basic idea - fixed height control bar at the top of a window, placed and sized in gui editor
|
|
// menu text for menus or menu items should not begin with a digit
|
|
// all menus can be removed via the clearMenus() console command
|
|
// each menu is added via the addMenu(menuText, menuId) console command
|
|
// each menu is added with a menu id
|
|
// menu items are added to menus via that addMenuItem(menu, menuItemText, menuItemId, accelerator, checkGroup) console command
|
|
// each menu item is added with a menu item id and an optional accelerator
|
|
// menu items are initially enabled, but can be disabled/re-enabled via the setMenuItemEnable(menu,menuItem,bool)
|
|
// menu text can be set via the setMenuText(menu, newMenuText) console method
|
|
// menu item text can be set via the setMenuItemText console method
|
|
// menu items can be removed via the removeMenuItem(menu, menuItem) console command
|
|
// menu items can be cleared via the clearMenuItems(menu) console command
|
|
// menus can be hidden or shown via the setMenuVisible(menu, bool) console command
|
|
// menu items can be hidden or shown via the setMenuItemVisible(menu, menuItem, bool) console command
|
|
// menu items can be check'd via the setMenuItemChecked(menu, menuItem, bool) console command
|
|
// if the bool is true, any other items in that menu item's check group become unchecked.
|
|
//
|
|
// menu items can have a bitmap set on them via the setMenuItemBitmap(menu, menuItem, bitmapIndex)
|
|
// passing -1 for the bitmap index will result in no bitmap being shown
|
|
// the index paramater is an index into the bitmap array of the associated profile
|
|
// this can be used, for example, to display a check next to a selected menu item
|
|
// bitmap indices are actually multiplied by 3 when indexing into the bitmap
|
|
// since bitmaps have normal, selected and disabled states.
|
|
//
|
|
// menus can be removed via the removeMenu console command
|
|
// specification arguments for menus and menu items can be either the id or the text of the menu or menu item
|
|
// adding the menu item "-" will add an un-selectable seperator to the menu
|
|
// callbacks:
|
|
// when a menu is clicked, before it is displayed, the menu calls its onMenuSelect(menuId, menuText) method -
|
|
// this allows the callback to enable/disable menu items, or add menu items in a context-sensitive way
|
|
// when a menu item is clicked, the menu removes itself from display, then calls onMenuItemSelect(menuId, menuText, menuItemId, menuItemText)
|
|
|
|
// the initial implementation does not support:
|
|
// hierarchal menus
|
|
// keyboard accelerators on menu text (i.e. via alt-key combos)
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CONOBJECT(GuiMenuBar);
|
|
|
|
//------------------------------------------------------------------------------
|
|
// console methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
ConsoleMethod(GuiMenuBar, clearMenus, void, 2, 2, "() - clears all the menus from the menu bar.")
|
|
{
|
|
object->clearMenus();
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, addMenu, void, 4, 4, "(string menuText, int menuId) - adds a new menu to the menu bar.")
|
|
{
|
|
if(dIsdigit(argv[2][0]))
|
|
{
|
|
Con::errorf("Cannot add menu %s (id = %s). First character of a menu's text cannot be a digit.", argv[2], argv[3]);
|
|
return;
|
|
}
|
|
object->addMenu(argv[2], dAtoi(argv[3]));
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, addMenuItem, void, 5, 7, "(string menu, string menuItemText, int menuItemId, string accelerator = NULL, int checkGroup = -1) - adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.")
|
|
{
|
|
if(dIsdigit(argv[3][0]))
|
|
{
|
|
Con::errorf("Cannot add menu item %s (id = %s). First character of a menu item's text cannot be a digit.", argv[3], argv[4]);
|
|
return;
|
|
}
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for addMenuItem.", argv[2]);
|
|
return;
|
|
}
|
|
object->addMenuItem(menu, argv[3], dAtoi(argv[4]), argc == 5 ? "" : argv[5], argc < 7 ? -1 : dAtoi(argv[6]));
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuItemEnable, void, 5, 5, "(string menu, string menuItem, bool enabled) - sets the menu item to enabled or disabled based on the enable parameter. The specified menu and menu item can either be text or ids.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuItemEnable.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for setMenuItemEnable.", argv[3]);
|
|
return;
|
|
}
|
|
menuItem->enabled = dAtob(argv[4]);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuItemChecked, void, 5, 5, "(string menu, string menuItem, bool checked) - sets the menu item bitmap to a check mark, which must be the first element in the bitmap array. Any other menu items in the menu with the same check group become unchecked if they are checked.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuItemChecked.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for setMenuItemChecked.", argv[3]);
|
|
return;
|
|
}
|
|
bool checked = dAtob(argv[4]);
|
|
if(checked && menuItem->checkGroup != -1)
|
|
{
|
|
// first, uncheck everything in the group:
|
|
for(GuiMenuBar::MenuItem *itemWalk = menu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem)
|
|
if(itemWalk->checkGroup == menuItem->checkGroup && itemWalk->bitmapIndex == 0)
|
|
itemWalk->bitmapIndex = -1;
|
|
}
|
|
menuItem->bitmapIndex = checked ? 0 : -1;
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuText, void, 4, 4, "(string menu, string newMenuText) - sets the text of the specified menu to the new string.")
|
|
{
|
|
if(dIsdigit(argv[3][0]))
|
|
{
|
|
Con::errorf("Cannot name menu %s to %s. First character of a menu's text cannot be a digit.", argv[2], argv[3]);
|
|
return;
|
|
}
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuText.", argv[2]);
|
|
return;
|
|
}
|
|
dFree(menu->text);
|
|
menu->text = dStrdup(argv[3]);
|
|
object->menuBarDirty = true;
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuVisible, void, 4, 4, "(string menu, bool visible) - sets the whether or not to display the specified menu.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuVisible.", argv[2]);
|
|
return;
|
|
}
|
|
menu->visible = dAtob(argv[3]);
|
|
object->menuBarDirty = true;
|
|
object->setUpdate();
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuItemText, void, 5, 5, "(string menu, string menuItem, string newMenuItemText) - sets the text of the specified menu item to the new string.")
|
|
{
|
|
if(dIsdigit(argv[4][0]))
|
|
{
|
|
Con::errorf("Cannot name menu item %s to %s. First character of a menu item's text cannot be a digit.", argv[3], argv[4]);
|
|
return;
|
|
}
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuItemText.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for setMenuItemText.", argv[3]);
|
|
return;
|
|
}
|
|
dFree(menuItem->text);
|
|
menuItem->text = dStrdup(argv[4]);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuItemVisible, void, 5, 5, "(string menu, string menuItem, bool isVisible) - sets the specified menu item to be either visible or not.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuItemVisible.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for setMenuItemVisible.", argv[3]);
|
|
return;
|
|
}
|
|
menuItem->visible = dAtob(argv[4]);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, setMenuItemBitmap, void, 5, 5, "(string menu, string menuItem, int bitmapIndex) - sets the specified menu item bitmap index in the bitmap array. Setting the item's index to -1 will remove any bitmap.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for setMenuItemBitmap.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for setMenuItemBitmap.", argv[3]);
|
|
return;
|
|
}
|
|
menuItem->bitmapIndex = dAtoi(argv[4]);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, removeMenuItem, void, 4, 4, "(string menu, string menuItem) - removes the specified menu item from the menu.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for removeMenuItem.", argv[2]);
|
|
return;
|
|
}
|
|
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
|
|
if(!menuItem)
|
|
{
|
|
Con::errorf("Cannot find menu item %s for removeMenuItem.", argv[3]);
|
|
return;
|
|
}
|
|
object->removeMenuItem(menu, menuItem);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, clearMenuItems, void, 3, 3, "(string menu) - removes all the menu items from the specified menu.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for clearMenuItems.", argv[2]);
|
|
return;
|
|
}
|
|
object->clearMenuItems(menu);
|
|
}
|
|
|
|
ConsoleMethod(GuiMenuBar, removeMenu, void, 3, 3, "(string menu) - removes the specified menu from the menu bar.")
|
|
{
|
|
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
|
|
if(!menu)
|
|
{
|
|
Con::errorf("Cannot find menu %s for removeMenu.", argv[2]);
|
|
return;
|
|
}
|
|
object->clearMenuItems(menu);
|
|
object->menuBarDirty = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// menu management methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
void GuiMenuBar::addMenu(const char *menuText, U32 menuId)
|
|
{
|
|
// allocate the menu
|
|
Menu *newMenu = new Menu;
|
|
newMenu->text = dStrdup(menuText);
|
|
newMenu->id = menuId;
|
|
newMenu->nextMenu = NULL;
|
|
newMenu->firstMenuItem = NULL;
|
|
newMenu->visible = true;
|
|
|
|
// add it to the menu list
|
|
menuBarDirty = true;
|
|
Menu **walk;
|
|
for(walk = &menuList; *walk; walk = &(*walk)->nextMenu)
|
|
;
|
|
*walk = newMenu;
|
|
}
|
|
|
|
GuiMenuBar::Menu *GuiMenuBar::findMenu(const char *menu)
|
|
{
|
|
if(dIsdigit(menu[0]))
|
|
{
|
|
U32 id = dAtoi(menu);
|
|
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
|
|
if(id == walk->id)
|
|
return walk;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
|
|
if(!dStricmp(menu, walk->text))
|
|
return walk;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
GuiMenuBar::MenuItem *GuiMenuBar::findMenuItem(Menu *menu, const char *menuItem)
|
|
{
|
|
if(dIsdigit(menuItem[0]))
|
|
{
|
|
U32 id = dAtoi(menuItem);
|
|
for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
|
|
if(id == walk->id)
|
|
return walk;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
|
|
if(!dStricmp(menuItem, walk->text))
|
|
return walk;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::removeMenu(Menu *menu)
|
|
{
|
|
menuBarDirty = true;
|
|
clearMenuItems(menu);
|
|
for(Menu **walk = &menuList; *walk; walk = &(*walk)->nextMenu)
|
|
{
|
|
if(*walk == menu)
|
|
{
|
|
*walk = menu->nextMenu;
|
|
break;
|
|
}
|
|
}
|
|
dFree(menu->text);
|
|
delete menu;
|
|
}
|
|
|
|
void GuiMenuBar::removeMenuItem(Menu *menu, MenuItem *menuItem)
|
|
{
|
|
for(MenuItem **walk = &menu->firstMenuItem; *walk; walk = &(*walk)->nextMenuItem)
|
|
{
|
|
if(*walk == menuItem)
|
|
{
|
|
*walk = menuItem->nextMenuItem;
|
|
break;
|
|
}
|
|
}
|
|
dFree(menuItem->text);
|
|
dFree(menuItem->accelerator);
|
|
delete menuItem;
|
|
}
|
|
|
|
void GuiMenuBar::addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup)
|
|
{
|
|
// allocate the new menu item
|
|
MenuItem *newMenuItem = new MenuItem;
|
|
newMenuItem->text = dStrdup(text);
|
|
if(accelerator[0])
|
|
newMenuItem->accelerator = dStrdup(accelerator);
|
|
else
|
|
newMenuItem->accelerator = NULL;
|
|
newMenuItem->id = id;
|
|
newMenuItem->checkGroup = checkGroup;
|
|
newMenuItem->nextMenuItem = NULL;
|
|
newMenuItem->acceleratorIndex = 0;
|
|
newMenuItem->enabled = text[0] != '-';
|
|
newMenuItem->visible = true;
|
|
newMenuItem->bitmapIndex = -1;
|
|
|
|
// link it into the menu's menu item list
|
|
MenuItem **walk = &menu->firstMenuItem;
|
|
while(*walk)
|
|
walk = &(*walk)->nextMenuItem;
|
|
*walk = newMenuItem;
|
|
|
|
}
|
|
|
|
void GuiMenuBar::clearMenuItems(Menu *menu)
|
|
{
|
|
while(menu->firstMenuItem)
|
|
removeMenuItem(menu, menu->firstMenuItem);
|
|
}
|
|
|
|
void GuiMenuBar::clearMenus()
|
|
{
|
|
while(menuList)
|
|
removeMenu(menuList);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// initialization, input and render methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
GuiMenuBar::GuiMenuBar()
|
|
{
|
|
menuList = NULL;
|
|
menuBarDirty = true;
|
|
mouseDownMenu = NULL;
|
|
mouseOverMenu = NULL;
|
|
mCurAcceleratorIndex = 0;
|
|
mBackground = NULL;
|
|
}
|
|
|
|
bool GuiMenuBar::onWake()
|
|
{
|
|
if(!Parent::onWake())
|
|
return false;
|
|
mProfile->constructBitmapArray(); // if a bitmap was specified...
|
|
maxBitmapSize.set(0,0);
|
|
S32 numBitmaps = mProfile->mBitmapArrayRects.size();
|
|
if(numBitmaps)
|
|
{
|
|
RectI *bitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
for(S32 i = 0; i < numBitmaps; i++)
|
|
{
|
|
if(bitmapBounds[i].extent.x > maxBitmapSize.x)
|
|
maxBitmapSize.x = bitmapBounds[i].extent.x;
|
|
if(bitmapBounds[i].extent.y > maxBitmapSize.y)
|
|
maxBitmapSize.y = bitmapBounds[i].extent.y;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GuiMenuBar::Menu *GuiMenuBar::findHitMenu(Point2I mousePoint)
|
|
{
|
|
Point2I pos = globalToLocalCoord(mousePoint);
|
|
|
|
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
|
|
if(walk->visible && walk->bounds.pointInRect(pos))
|
|
return walk;
|
|
return NULL;
|
|
}
|
|
|
|
void GuiMenuBar::onPreRender()
|
|
{
|
|
Parent::onPreRender();
|
|
if(menuBarDirty)
|
|
{
|
|
menuBarDirty = false;
|
|
U32 curX = 0;
|
|
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
|
|
{
|
|
if(!walk->visible)
|
|
continue;
|
|
walk->bounds.set(curX, 1, mProfile->mFont->getStrWidth((const UTF8 *)walk->text) + 12, mBounds.extent.y - 2);
|
|
curX += walk->bounds.extent.x;
|
|
}
|
|
mouseOverMenu = NULL;
|
|
mouseDownMenu = NULL;
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::checkMenuMouseMove(const GuiEvent &event)
|
|
{
|
|
Menu *hit = findHitMenu(event.mousePoint);
|
|
if(hit && hit != mouseDownMenu)
|
|
{
|
|
// gotta close out the current menu...
|
|
mTextList->setSelectedCell(Point2I(-1, -1));
|
|
closeMenu();
|
|
mouseOverMenu = mouseDownMenu = hit;
|
|
setUpdate();
|
|
onAction();
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::onMouseMove(const GuiEvent &event)
|
|
{
|
|
Menu *hit = findHitMenu(event.mousePoint);
|
|
if(hit != mouseOverMenu)
|
|
{
|
|
mouseOverMenu = hit;
|
|
setUpdate();
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::onMouseLeave(const GuiEvent &event)
|
|
{
|
|
if(mouseOverMenu)
|
|
setUpdate();
|
|
mouseOverMenu = NULL;
|
|
}
|
|
|
|
void GuiMenuBar::onMouseDragged(const GuiEvent &event)
|
|
{
|
|
Menu *hit = findHitMenu(event.mousePoint);
|
|
|
|
if(hit != mouseOverMenu)
|
|
{
|
|
mouseOverMenu = hit;
|
|
mouseDownMenu = hit;
|
|
setUpdate();
|
|
onAction();
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::onMouseDown(const GuiEvent &event)
|
|
{
|
|
mouseDownMenu = mouseOverMenu = findHitMenu(event.mousePoint);
|
|
setUpdate();
|
|
onAction();
|
|
}
|
|
|
|
void GuiMenuBar::onMouseUp(const GuiEvent &event)
|
|
{
|
|
mouseDownMenu = NULL;
|
|
setUpdate();
|
|
}
|
|
|
|
void GuiMenuBar::onRender(Point2I offset, const RectI &updateRect)
|
|
{
|
|
//if opaque, fill the update rect with the fill color
|
|
if (mProfile->mOpaque)
|
|
dglDrawRectFill(RectI(offset, mBounds.extent), mProfile->mFillColor);
|
|
|
|
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
|
|
{
|
|
if(!walk->visible)
|
|
continue;
|
|
ColorI fontColor = mProfile->mFontColor;
|
|
RectI bounds = walk->bounds;
|
|
bounds.point += offset;
|
|
|
|
Point2I start;
|
|
|
|
start.x = walk->bounds.point.x + 6;
|
|
start.y = walk->bounds.point.y + ( walk->bounds.extent.y - ( mProfile->mFont->getHeight() - 2 ) ) / 2;
|
|
|
|
if(walk == mouseDownMenu)
|
|
renderSlightlyLoweredBox(bounds, mProfile);
|
|
else if(walk == mouseOverMenu && mouseDownMenu == NULL)
|
|
renderSlightlyRaisedBox(bounds, mProfile);
|
|
|
|
dglSetBitmapModulation( fontColor );
|
|
|
|
dglDrawText( mProfile->mFont, start + offset, walk->text, mProfile->mFontColors );
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::buildAcceleratorMap()
|
|
{
|
|
Parent::buildAcceleratorMap();
|
|
// ok, accelerator map is cleared...
|
|
// add all our keys:
|
|
mCurAcceleratorIndex = 1;
|
|
|
|
for(Menu *menu = menuList; menu; menu = menu->nextMenu)
|
|
{
|
|
for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem)
|
|
{
|
|
if(!item->accelerator)
|
|
{
|
|
item->accelerator = 0;
|
|
continue;
|
|
}
|
|
EventDescriptor accelEvent;
|
|
ActionMap::createEventDescriptor(item->accelerator, &accelEvent);
|
|
|
|
//now we have a modifier, and a key, add them to the canvas
|
|
GuiCanvas *root = getRoot();
|
|
if (root)
|
|
root->addAcceleratorKey(this, mCurAcceleratorIndex, accelEvent.eventCode, accelEvent.flags);
|
|
item->acceleratorIndex = mCurAcceleratorIndex;
|
|
mCurAcceleratorIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiMenuBar::acceleratorKeyPress(U32 index)
|
|
{
|
|
// loop through all the menus
|
|
// and find the item that corresponds to the accelerator index
|
|
for(Menu *menu = menuList; menu; menu = menu->nextMenu)
|
|
{
|
|
if(!menu->visible)
|
|
continue;
|
|
|
|
for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem)
|
|
{
|
|
if(item->acceleratorIndex == index)
|
|
{
|
|
// first, call the script callback for menu selection:
|
|
Con::executef( this, 4, "onMenuSelect", Con::getIntArg(menu->id),
|
|
menu->text);
|
|
if(item->visible)
|
|
menuItemSelected(menu, item);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Menu display class methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
GuiMenuBackgroundCtrl::GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList)
|
|
{
|
|
mMenuBarCtrl = ctrl;
|
|
mTextList = textList;
|
|
}
|
|
|
|
void GuiMenuBackgroundCtrl::onMouseDown(const GuiEvent &event)
|
|
{
|
|
mTextList->setSelectedCell(Point2I(-1,-1));
|
|
mMenuBarCtrl->closeMenu();
|
|
}
|
|
|
|
void GuiMenuBackgroundCtrl::onMouseMove(const GuiEvent &event)
|
|
{
|
|
GuiCanvas *root = getRoot();
|
|
GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
|
|
if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
|
|
mMenuBarCtrl->checkMenuMouseMove(event);
|
|
}
|
|
|
|
void GuiMenuBackgroundCtrl::onMouseDragged(const GuiEvent &event)
|
|
{
|
|
GuiCanvas *root = getRoot();
|
|
GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
|
|
if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
|
|
mMenuBarCtrl->checkMenuMouseMove(event);
|
|
}
|
|
|
|
GuiMenuTextListCtrl::GuiMenuTextListCtrl(GuiMenuBar *ctrl)
|
|
{
|
|
mMenuBarCtrl = ctrl;
|
|
}
|
|
|
|
void GuiMenuTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
|
|
{
|
|
if(dStrcmp(mList[cell.y].text + 2, "-\t"))
|
|
Parent::onRenderCell(offset, cell, selected, mouseOver);
|
|
else
|
|
{
|
|
S32 yp = offset.y + mCellSize.y / 2;
|
|
dglDrawLine(offset.x, yp, offset.x + mCellSize.x, yp, ColorI(128,128,128));
|
|
dglDrawLine(offset.x, yp+1, offset.x + mCellSize.x, yp+1, ColorI(255,255,255));
|
|
}
|
|
// now see if there's a bitmap...
|
|
U8 idx = mList[cell.y].text[0];
|
|
if(idx != 1)
|
|
{
|
|
// there's a bitmap...
|
|
U32 index = U32(idx - 2) * 3;
|
|
if(!mList[cell.y].active)
|
|
index += 2;
|
|
else if(selected || mouseOver)
|
|
index ++;
|
|
|
|
RectI rect = mProfile->mBitmapArrayRects[index];
|
|
Point2I off = mMenuBarCtrl->maxBitmapSize - rect.extent;
|
|
off /= 2;
|
|
|
|
dglClearBitmapModulation();
|
|
dglDrawBitmapSR(mProfile->mTextureHandle, offset + off, rect);
|
|
}
|
|
}
|
|
|
|
bool GuiMenuTextListCtrl::onKeyDown(const GuiEvent &event)
|
|
{
|
|
//if the control is a dead end, don't process the input:
|
|
if ( !mVisible || !mActive || !mAwake )
|
|
return false;
|
|
|
|
//see if the key down is a <return> or not
|
|
if ( event.modifier == 0 )
|
|
{
|
|
if ( event.keyCode == KEY_RETURN )
|
|
{
|
|
mMenuBarCtrl->closeMenu();
|
|
return true;
|
|
}
|
|
else if ( event.keyCode == KEY_ESCAPE )
|
|
{
|
|
mSelectedCell.set( -1, -1 );
|
|
mMenuBarCtrl->closeMenu();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//otherwise, pass the event to it's parent
|
|
return Parent::onKeyDown(event);
|
|
}
|
|
|
|
void GuiMenuTextListCtrl::onMouseDown(const GuiEvent &event)
|
|
{
|
|
Parent::onMouseDown(event);
|
|
mMenuBarCtrl->closeMenu();
|
|
}
|
|
|
|
void GuiMenuTextListCtrl::onMouseUp(const GuiEvent &event)
|
|
{
|
|
// ok, this is kind of strange... but!
|
|
// here's the deal: if we get a mouse up in this control
|
|
// it means the mouse was dragged from the initial menu mouse click
|
|
// so: activate the menu result as though this event were,
|
|
// instead, a mouse down.
|
|
|
|
onMouseDown(event);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void GuiMenuBar::menuItemSelected(GuiMenuBar::Menu *menu, GuiMenuBar::MenuItem *item)
|
|
{
|
|
if(item->enabled)
|
|
Con::executef( this, 6, "onMenuItemSelect", Con::getIntArg(menu->id),
|
|
menu->text, Con::getIntArg(item->id), item->text);
|
|
}
|
|
|
|
void GuiMenuBar::onSleep()
|
|
{
|
|
if(mBackground) // a menu is up?
|
|
{
|
|
mTextList->setSelectedCell(Point2I(-1, -1));
|
|
closeMenu();
|
|
}
|
|
Parent::onSleep();
|
|
}
|
|
|
|
void GuiMenuBar::closeMenu()
|
|
{
|
|
// Get the selection from the text list:
|
|
S32 selectionIndex = mTextList->getSelectedCell().y;
|
|
|
|
// Pop the background:
|
|
getRoot()->popDialogControl(mBackground);
|
|
|
|
// Kill the popup:
|
|
mBackground->deleteObject();
|
|
mBackground = NULL;
|
|
|
|
// Now perform the popup action:
|
|
if ( selectionIndex != -1 )
|
|
{
|
|
MenuItem *list = mouseDownMenu->firstMenuItem;
|
|
|
|
while(selectionIndex && list)
|
|
{
|
|
list = list->nextMenuItem;
|
|
selectionIndex--;
|
|
}
|
|
if(list)
|
|
menuItemSelected(mouseDownMenu, list);
|
|
}
|
|
mouseDownMenu = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiMenuBar::onAction()
|
|
{
|
|
if(!mouseDownMenu)
|
|
return;
|
|
|
|
// first, call the script callback for menu selection:
|
|
Con::executef( this, 4, "onMenuSelect", Con::getIntArg(mouseDownMenu->id),
|
|
mouseDownMenu->text);
|
|
|
|
MenuItem *visWalk = mouseDownMenu->firstMenuItem;
|
|
while(visWalk)
|
|
{
|
|
if(visWalk->visible)
|
|
break;
|
|
visWalk = visWalk->nextMenuItem;
|
|
}
|
|
if(!visWalk)
|
|
{
|
|
mouseDownMenu = NULL;
|
|
return;
|
|
}
|
|
|
|
mTextList = new GuiMenuTextListCtrl(this);
|
|
mTextList->mProfile = mProfile;
|
|
|
|
mBackground = new GuiMenuBackgroundCtrl(this, mTextList);
|
|
|
|
GuiCanvas *root = getRoot();
|
|
Point2I windowExt = root->mBounds.extent;
|
|
|
|
mBackground->mBounds.point.set(0,0);
|
|
mBackground->mBounds.extent = root->mBounds.extent;
|
|
|
|
S32 textWidth = 0, width = 0;
|
|
S32 acceleratorWidth = 0;
|
|
|
|
GFont *font = mProfile->mFont;
|
|
|
|
for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
|
|
{
|
|
if(!walk->visible)
|
|
continue;
|
|
|
|
S32 iTextWidth = font->getStrWidth((const UTF8 *)walk->text);
|
|
S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth((const UTF8 *)walk->accelerator) : 0;
|
|
|
|
if(iTextWidth > textWidth)
|
|
textWidth = iTextWidth;
|
|
if(iAcceleratorWidth > acceleratorWidth)
|
|
acceleratorWidth = iAcceleratorWidth;
|
|
}
|
|
width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4;
|
|
|
|
mTextList->setCellSize(Point2I(width, font->getHeight()+3));
|
|
mTextList->clearColumnOffsets();
|
|
mTextList->addColumnOffset(-1); // add an empty column in for the bitmap index.
|
|
mTextList->addColumnOffset(maxBitmapSize.x + 1);
|
|
mTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4);
|
|
|
|
U32 entryCount = 0;
|
|
|
|
for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
|
|
{
|
|
if(!walk->visible)
|
|
continue;
|
|
|
|
char buf[512];
|
|
char bitmapIndex = 1;
|
|
if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size()))
|
|
bitmapIndex = walk->bitmapIndex + 2;
|
|
dSprintf(buf, sizeof(buf), "%c\t%s\t%s", bitmapIndex, walk->text, walk->accelerator ? walk->accelerator : "");
|
|
mTextList->addEntry(entryCount, buf);
|
|
|
|
if(!walk->enabled)
|
|
mTextList->setEntryActive(entryCount, false);
|
|
|
|
entryCount++;
|
|
}
|
|
Point2I menuPoint = localToGlobalCoord(mouseDownMenu->bounds.point);
|
|
menuPoint.y += mouseDownMenu->bounds.extent.y + 2;
|
|
|
|
GuiControl *ctrl = new GuiControl;
|
|
ctrl->mBounds.point = menuPoint;
|
|
ctrl->mBounds.extent = mTextList->mBounds.extent + Point2I(6, 6);
|
|
ctrl->mProfile = mProfile;
|
|
mTextList->mBounds.point += Point2I(3,3);
|
|
|
|
//mTextList->mBounds.point = Point2I(3,3);
|
|
|
|
mTextList->registerObject();
|
|
mBackground->registerObject();
|
|
ctrl->registerObject();
|
|
|
|
mBackground->addObject( ctrl );
|
|
ctrl->addObject( mTextList );
|
|
|
|
root->pushDialogControl(mBackground, mLayer + 1);
|
|
mTextList->setFirstResponder();
|
|
}
|
|
|
|
|