1 Star 0 Fork 0

yoyojacky/CSMoE

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
Menu.cpp 72.50 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553
//========= Copyright ?1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <tier1/KeyValues.h>
#include <vgui/IPanel.h>
#include <vgui/IInputInternal.h>
#include <vgui/ISurface.h>
#include <vgui/IVGUI.h>
#include "Controls.h"
#include "MenuItem.h"
#include "MenuButton.h"
#include "Menu.h"
#include "TextImage.h"
#include "ScrollBar.h"
// memdbgon must be the last include file in a .cpp file
#include "tier0/memdbgon.h"
#define MENU_SEPARATOR_HEIGHT 3
#include "wctype.h"
using namespace vgui2;
//-----------------------------------------------------------------------------
// Purpose: divider line in a menu
//-----------------------------------------------------------------------------
class vgui2::MenuSeparator : public Panel
{
public:
DECLARE_CLASS_SIMPLE(MenuSeparator, Panel);
MenuSeparator(Panel *parent, char const *panelName) :
BaseClass(parent, panelName)
{
SetPaintEnabled(true);
SetPaintBackgroundEnabled(true);
SetPaintBorderEnabled(false);
}
virtual void Paint()
{
int w, h;
GetSize(w, h);
surface()->DrawSetColor(GetFgColor());
surface()->DrawFilledRect(4, 1, w - 1, 2);
}
virtual void ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
SetFgColor(pScheme->GetColor("Menu.SeparatorColor", Color(142, 142, 142, 255)));
SetBgColor(pScheme->GetColor("Menu.BgColor", Color(0, 0, 0, 255)));
}
};
DECLARE_BUILD_FACTORY(Menu);
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
Menu::Menu(Panel *parent, const char *panelName) : Panel(parent, panelName)
{
m_Alignment = Label::a_west;
m_iFixedWidth = 0;
m_iMinimumWidth = 0;
m_iNumVisibleLines = -1; // No limit
m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
m_pScroller = new ScrollBar(this, "MenuScrollBar", true);
m_pScroller->SetVisible(false);
m_pScroller->AddActionSignalTarget(this);
_sizedForScrollBar = false;
SetZPos(1);
SetVisible(false);
MakePopup(false);
SetParent(parent);
_recalculateWidth = true;
m_iInputMode = MOUSE;
m_iCheckImageWidth = 0;
m_iActivatedItem = 0;
m_bUseFallbackFont = false;
m_hFallbackItemFont = INVALID_FONT;
m_bImageBackground = false;
if (IsProportional())
{
m_iMenuItemHeight = scheme()->GetProportionalScaledValueEx(GetScheme(), DEFAULT_MENU_ITEM_HEIGHT);
}
else
{
m_iMenuItemHeight = DEFAULT_MENU_ITEM_HEIGHT;
}
m_hItemFont = INVALID_FONT;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
Menu::~Menu()
{
delete m_pScroller;
}
//-----------------------------------------------------------------------------
// Purpose: Remove all menu items from the menu.
//-----------------------------------------------------------------------------
void Menu::DeleteAllItems()
{
FOR_EACH_LL(m_MenuItems, i)
{
m_MenuItems[i]->MarkForDeletion();
}
m_MenuItems.RemoveAll();
m_SortedItems.RemoveAll();
m_VisibleSortedItems.RemoveAll();
m_Separators.RemoveAll();
int c = m_SeparatorPanels.Count();
for (int i = 0; i < c; ++i)
{
m_SeparatorPanels[i]->MarkForDeletion();
}
m_SeparatorPanels.RemoveAll();
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(MenuItem *panel)
{
panel->SetParent(this);
MEM_ALLOC_CREDIT();
int itemID = m_MenuItems.AddToTail(panel);
m_SortedItems.AddToTail(itemID);
InvalidateLayout(false);
_recalculateWidth = true;
panel->SetContentAlignment(m_Alignment);
if (INVALID_FONT != m_hItemFont)
{
panel->SetFont(m_hItemFont);
}
if (m_bUseFallbackFont && INVALID_FONT != m_hFallbackItemFont)
{
Label *l = panel;
TextImage *ti = l->GetTextImage();
if (ti)
{
ti->SetUseFallbackFont(m_bUseFallbackFont, m_hFallbackItemFont);
}
}
return itemID;
}
//-----------------------------------------------------------------------------
// Remove a single item
//-----------------------------------------------------------------------------
void Menu::DeleteItem(int itemID)
{
// FIXME: This doesn't work with separator panels yet
Assert(m_SeparatorPanels.Count() == 0);
m_MenuItems[itemID]->MarkForDeletion();
m_MenuItems.Remove(itemID);
m_SortedItems.FindAndRemove(itemID);
m_VisibleSortedItems.FindAndRemove(itemID);
InvalidateLayout(false);
_recalculateWidth = true;
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *item - MenuItem
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// *userData - any user data associated with this menu item
// Output: itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData)
{
item->SetCommand(command);
item->AddActionSignalTarget(target);
item->SetUserData(userData);
return AddMenuItem(item);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
// Output: itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItemKeyValuesCommand(MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData)
{
item->SetCommand(message);
item->AddActionSignalTarget(target);
item->SetUserData(userData);
return AddMenuItem(item);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// Output: itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText);
return AddMenuItemCharCommand(item, command, target, userData);
}
int Menu::AddMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText);
return AddMenuItemCharCommand(item, command, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// Output: itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
return AddMenuItem(itemText, itemText, command, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
int Menu::AddMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
return AddMenuItem(itemText, itemText, message, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be the text of the command sent when the
// item is selected.
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, Panel *target, const KeyValues *userData)
{
return AddMenuItem(itemText, itemText, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
return AddMenuItemCharCommand(item, command, target, userData);
}
int Menu::AddCheckableMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
return AddMenuItemCharCommand(item, command, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
return AddCheckableMenuItem(itemText, itemText, command, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
int Menu::AddCheckableMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
return AddCheckableMenuItem(itemText, itemText, message, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be the text of the command sent when the
// item is selected.
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, Panel *target, const KeyValues *userData)
{
return AddCheckableMenuItem(itemText, itemText, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu);
return AddMenuItemCharCommand(item, command, target, userData);
}
int Menu::AddCascadingMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu);
return AddMenuItemCharCommand(item, command, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *command - Command text to be sent when menu item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
return AddCascadingMenuItem(itemText, itemText, command, target, cascadeMenu, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input : *itemName - Name of item
// *itemText - Name of item text that will appear in the manu.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
int Menu::AddCascadingMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu);
return AddMenuItemKeyValuesCommand(item, message, target, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be used as the name of the menu item panel.
// *message - pointer to the message to send when the item is selected
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
return AddCascadingMenuItem(itemText, itemText, message, target, cascadeMenu, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input : *itemText - Name of item text that will appear in the manu.
// This will also be the text of the command sent when the
// item is selected.
// *target - Target panel of the command
// *cascadeMenu - if the menu item opens a cascading menu, this is a
// ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
return AddCascadingMenuItem(itemText, itemText, target, cascadeMenu, userData);
}
//-----------------------------------------------------------------------------
// Purpose: Sets the values of a menu item at the specified index
// Input : index - the index of this item entry
// *message - pointer to the message to send when the item is selected
//-----------------------------------------------------------------------------
void Menu::UpdateMenuItem(int itemID, const char *itemText, KeyValues *message, const KeyValues *userData)
{
Assert(m_MenuItems.IsValidIndex(itemID));
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
// make sure its enabled since disabled items get highlighted.
if (menuItem)
{
menuItem->SetText(itemText);
menuItem->SetCommand(message);
if (userData)
{
menuItem->SetUserData(userData);
}
}
}
_recalculateWidth = true;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the values of a menu item at the specified index
//-----------------------------------------------------------------------------
void Menu::UpdateMenuItem(int itemID, const wchar_t *wszItemText, KeyValues *message, const KeyValues *userData)
{
Assert(m_MenuItems.IsValidIndex(itemID));
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
// make sure its enabled since disabled items get highlighted.
if (menuItem)
{
menuItem->SetText(wszItemText);
menuItem->SetCommand(message);
if (userData)
{
menuItem->SetUserData(userData);
}
}
}
_recalculateWidth = true;
}
//-----------------------------------------------------------------------------
// Sets the content alignment of all items in the menu
//-----------------------------------------------------------------------------
void Menu::SetContentAlignment(Label::Alignment alignment)
{
if (m_Alignment != alignment)
{
m_Alignment = alignment;
// Change the alignment of existing menu items
int nCount = m_MenuItems.Count();
for (int i = 0; i < nCount; ++i)
{
m_MenuItems[i]->SetContentAlignment(alignment);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Locks down a specific width
//-----------------------------------------------------------------------------
void Menu::SetFixedWidth(int width)
{
// the padding makes it so the menu has the label padding on each side of the menu.
// makes the menu items look centered.
m_iFixedWidth = width;
InvalidateLayout(false);
}
//-----------------------------------------------------------------------------
// Purpose: sets the height of each menu item
//-----------------------------------------------------------------------------
void Menu::SetMenuItemHeight(int itemHeight)
{
m_iMenuItemHeight = itemHeight;
}
int Menu::GetMenuItemHeight() const
{
return m_iMenuItemHeight;
}
int Menu::CountVisibleItems()
{
int count = 0;
int c = m_SortedItems.Count();
for (int i = 0; i < c; ++i)
{
if (m_MenuItems[m_SortedItems[i]]->IsVisible())
++count;
}
return count;
}
void Menu::ComputeWorkspaceSize(int& workWide, int& workTall)
{
// make sure we factor in insets
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
int workX, workY;
surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);
workTall -= 20;
workTall -= itop;
workTall -= ibottom;
}
// Assumes relative coords in screenspace
void Menu::PositionRelativeToPanel(Panel *relative, MenuDirection_e direction, int nAdditionalYOffset /*=0*/, bool showMenu /*=false*/)
{
Assert(relative);
int rx, ry, rw, rh;
relative->GetBounds(rx, ry, rw, rh);
relative->LocalToScreen(rx, ry);
if (direction == CURSOR)
{
// force the menu to appear where the mouse button was pressed
input()->GetCursorPos(rx, ry);
rw = rh = 0;
}
else if (direction == ALIGN_WITH_PARENT && relative->GetVParent())
{
rx = 0, ry = 0;
relative->ParentLocalToScreen(rx, ry);
rx -= 1; // take border into account
ry += rh + nAdditionalYOffset;
rw = rh = 0;
}
else
{
rx = 0, ry = 0;
relative->LocalToScreen(rx, ry);
}
int workWide, workTall;
ComputeWorkspaceSize(workWide, workTall);
// Final pos
int x = 0, y = 0;
int mWide, mTall;
GetSize(mWide, mTall);
switch (direction)
{
case Menu::UP: // Menu prefers to open upward
{
x = rx;
int topOfReference = ry;
y = topOfReference - mTall;
if (y < 0)
{
int bottomOfReference = ry + rh + 1;
int remainingPixels = workTall - bottomOfReference;
// Can't fit on bottom, either, move to side
if (mTall >= remainingPixels)
{
y = workTall - mTall;
x = rx + rw;
// Try and place it to the left of the button
if (x + mWide > workWide)
{
x = rx - mWide;
}
}
else
{
// Room at bottom
y = bottomOfReference;
}
}
}
break;
// Everyone else aligns downward...
default:
case Menu::LEFT:
case Menu::RIGHT:
case Menu::DOWN:
{
x = rx;
int bottomOfReference = ry + rh + 1;
y = bottomOfReference;
if (bottomOfReference + mTall >= workTall)
{
// See if there's run straight above
if (mTall >= ry) // No room, try and push menu to right or left
{
y = workTall - mTall;
x = rx + rw;
// Try and place it to the left of the button
if (x + mWide > workWide)
{
x = rx - mWide;
}
}
else
{
// Room at top
y = ry - mTall;
}
}
}
break;
}
// Check left rightness
if (x + mWide > workWide)
{
x = workWide - mWide;
Assert(x >= 0); // yikes!!!
}
else if (x < 0)
{
x = 0;
}
SetPos(x, y);
if (showMenu)
{
SetVisible(true);
}
}
int Menu::ComputeFullMenuHeightWithInsets()
{
// make sure we factor in insets
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
int separatorHeight = 3;
// add up the size of all the child panels
// move the child panels to the correct place in the menu
int totalTall = itop + ibottom;
int i;
for (i = 0; i < m_SortedItems.Count(); i++) // use sortedItems instead of MenuItems due to SetPos()
{
int itemId = m_SortedItems[i];
MenuItem *child = m_MenuItems[itemId];
Assert(child);
if (!child)
continue;
// These should all be visible at this point
if (!child->IsVisible())
continue;
totalTall += m_iMenuItemHeight;
// Add a separator if needed...
int sepIndex = m_Separators.Find(itemId);
if (sepIndex != m_Separators.InvalidIndex())
{
totalTall += separatorHeight;
}
}
return totalTall;
}
//-----------------------------------------------------------------------------
// Purpose: Reformat according to the new layout
//-----------------------------------------------------------------------------
void Menu::PerformLayout()
{
MenuItem *parent = GetParentMenuItem();
bool cascading = parent != NULL ? true : false;
// make sure we factor in insets
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
int workWide, workTall;
ComputeWorkspaceSize(workWide, workTall);
int fullHeightWouldRequire = ComputeFullMenuHeightWithInsets();
bool bNeedScrollbar = fullHeightWouldRequire >= workTall;
int maxVisibleItems = CountVisibleItems();
if (m_iNumVisibleLines > 0 &&
maxVisibleItems > m_iNumVisibleLines)
{
bNeedScrollbar = true;
maxVisibleItems = m_iNumVisibleLines;
}
// if we have a scroll bar
if (bNeedScrollbar)
{
// add it to the display
AddScrollBar();
// This fills in m_VisibleSortedItems as needed
MakeItemsVisibleInScrollRange(m_iNumVisibleLines, min(fullHeightWouldRequire, workTall));
}
else
{
RemoveScrollBar();
// Make everything visible
m_VisibleSortedItems.RemoveAll();
int i;
int c = m_SortedItems.Count();
for (i = 0; i < c; ++i)
{
int itemID = m_SortedItems[i];
MenuItem *child = m_MenuItems[itemID];
if (!child || !child->IsVisible())
continue;
m_VisibleSortedItems.AddToTail(itemID);
}
// Hide the separators, the needed ones will be readded below
c = m_SeparatorPanels.Count();
for (i = 0; i < c; ++i)
{
if (m_SeparatorPanels[i])
{
m_SeparatorPanels[i]->SetVisible(false);
}
}
}
// get the appropriate menu border
LayoutMenuBorder();
int trueW = GetWide();
if (bNeedScrollbar)
{
trueW -= m_pScroller->GetWide();
}
int separatorHeight = MENU_SEPARATOR_HEIGHT;
// add up the size of all the child panels
// move the child panels to the correct place in the menu
int menuTall = 0;
int totalTall = itop + ibottom;
int i;
for (i = 0; i < m_VisibleSortedItems.Count(); i++) // use sortedItems instead of MenuItems due to SetPos()
{
int itemId = m_VisibleSortedItems[i];
MenuItem *child = m_MenuItems[itemId];
Assert(child);
if (!child)
continue;
// These should all be visible at this point
if (!child->IsVisible())
continue;
if (totalTall >= workTall)
break;
if (INVALID_FONT != m_hItemFont)
{
child->SetFont(m_hItemFont);
}
// take into account inset
child->SetPos(0, menuTall);
child->SetTall(m_iMenuItemHeight); // Width is set in a second pass
menuTall += m_iMenuItemHeight;
totalTall += m_iMenuItemHeight;
// this will make all the menuitems line up in a column with space for the checks to the left.
if ((!child->IsCheckable()) && (m_iCheckImageWidth > 0))
{
// Non checkable items have to move over
child->SetTextInset(m_iCheckImageWidth, 0);
}
else if (child->IsCheckable())
{
child->SetTextInset(0, 0); //TODO: for some reason I can't comment this out.
}
// Add a separator if needed...
int sepIndex = m_Separators.Find(itemId);
if (sepIndex != m_Separators.InvalidIndex())
{
MenuSeparator *sep = m_SeparatorPanels[sepIndex];
Assert(sep);
sep->SetVisible(true);
sep->SetBounds(0, menuTall, trueW, separatorHeight);
menuTall += separatorHeight;
totalTall += separatorHeight;
}
}
if (!m_iFixedWidth)
{
_recalculateWidth = true;
CalculateWidth();
}
else if (m_iFixedWidth)
{
_menuWide = m_iFixedWidth;
// fixed width menus include the scroll bar in their width.
if (_sizedForScrollBar)
{
_menuWide -= m_pScroller->GetWide();
}
}
SizeMenuItems();
int extraWidth = 0;
if (_sizedForScrollBar)
{
extraWidth = m_pScroller->GetWide();
}
int mwide = _menuWide + extraWidth;
if (mwide > workWide)
{
mwide = workWide;
}
int mtall = menuTall + itop + ibottom;
if (mtall > workTall)
{
// Shouldn't happen
mtall = workTall;
}
// set the new size of the menu
SetSize(mwide, mtall);
// move the menu to the correct position if it is a cascading menu.
if (cascading)
{
// move the menu to the correct position if it is a cascading menu.
PositionCascadingMenu();
}
// set up scroll bar as appropriate
if (m_pScroller->IsVisible())
{
LayoutScrollBar();
}
FOR_EACH_LL(m_MenuItems, j)
{
m_MenuItems[j]->InvalidateLayout(); // cause each menu item to redo its apply settings now we have sized ourselves
}
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Force the menu to work out how wide it should be
//-----------------------------------------------------------------------------
void Menu::ForceCalculateWidth()
{
_recalculateWidth = true;
CalculateWidth();
PerformLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Figure out how wide the menu should be if the menu is not fixed width
//-----------------------------------------------------------------------------
void Menu::CalculateWidth()
{
if (!_recalculateWidth)
return;
_menuWide = 0;
if (!m_iFixedWidth)
{
// find the biggest menu item
FOR_EACH_LL(m_MenuItems, i)
{
int wide, tall;
m_MenuItems[i]->GetContentSize(wide, tall);
if (wide > _menuWide - Label::Content)
{
_menuWide = wide + Label::Content;
}
}
}
// enfoce a minimumWidth
if (_menuWide < m_iMinimumWidth)
{
_menuWide = m_iMinimumWidth;
}
_recalculateWidth = false;
}
//-----------------------------------------------------------------------------
// Purpose: Set up the scroll bar attributes,size and location.
//-----------------------------------------------------------------------------
void Menu::LayoutScrollBar()
{
//!! need to make it recalculate scroll positions
m_pScroller->SetEnabled(false);
m_pScroller->SetRangeWindow(m_VisibleSortedItems.Count());
m_pScroller->SetRange(0, CountVisibleItems());
m_pScroller->SetButtonPressedScrollValue(1);
int wide, tall;
GetSize(wide, tall);
// make sure we factor in insets
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
// with a scroll bar we take off the inset
wide -= iright;
m_pScroller->SetPos(wide - m_pScroller->GetWide(), 1);
// scrollbar is inside the menu's borders.
m_pScroller->SetSize(m_pScroller->GetWide(), tall - ibottom - itop);
}
//-----------------------------------------------------------------------------
// Purpose: Figure out where to open menu if it is a cascading menu
//-----------------------------------------------------------------------------
void Menu::PositionCascadingMenu()
{
Assert(GetVParent());
int parentX, parentY, parentWide, parentTall;
// move the menu to the correct place below the menuItem
ipanel()->GetSize(GetVParent(), parentWide, parentTall);
ipanel()->GetPos(GetVParent(), parentX, parentY);
parentX += parentWide, parentY = 0;
ParentLocalToScreen(parentX, parentY);
SetPos(parentX, parentY);
// for cascading menus,
// make sure we're on the screen
int workX, workY, workWide, workTall, x, y, wide, tall;
GetBounds(x, y, wide, tall);
surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);
if (x + wide > workX + workWide)
{
// we're off the right, move the menu to the left side
// orignalX - width of the parentmenuitem - width of this menu.
// add 2 pixels to offset one pixel onto the parent menu.
x -= (parentWide + wide);
x -= 2;
}
else
{
// alignment move it in the amount of the insets.
x += 1;
}
if (y + tall > workY + workTall)
{
int lastWorkY = workY + workTall;
int pixelsOffBottom = (y + tall) - lastWorkY;
y -= pixelsOffBottom;
y -= 2;
}
else
{
y -= 1;
}
SetPos(x, y);
MoveToFront();
}
//-----------------------------------------------------------------------------
// Purpose: Size the menu items so they are the width of the menu.
// Also size the menu items with cascading menus so the arrow fits in there.
//-----------------------------------------------------------------------------
void Menu::SizeMenuItems()
{
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
// assign the sizes of all the menu item panels
FOR_EACH_LL(m_MenuItems, i)
{
MenuItem *child = m_MenuItems[i];
if (child)
{
// labels do thier own sizing. this will size the label to the width of the menu,
// this will put the cascading menu arrow on the right side automatically.
child->SetWide(_menuWide - ileft - iright);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Makes menu items visible in relation to where the scroll bar is
//-----------------------------------------------------------------------------
void Menu::MakeItemsVisibleInScrollRange(int maxVisibleItems, int nNumPixelsAvailable)
{
// Detach all items from tree
int i;
FOR_EACH_LL(m_MenuItems, item)
{
m_MenuItems[item]->SetBounds(0, 0, 0, 0);
}
for (i = 0; i < m_SeparatorPanels.Count(); ++i)
{
m_SeparatorPanels[i]->SetVisible(false);
}
m_VisibleSortedItems.RemoveAll();
int tall = 0;
int startItem = m_pScroller->GetValue();
Assert(startItem >= 0);
do
{
if (startItem >= m_SortedItems.Count())
break;
int itemId = m_SortedItems[startItem];
if (!m_MenuItems[itemId]->IsVisible())
{
++startItem;
continue;
}
int itemHeight = m_iMenuItemHeight;
int sepIndex = m_Separators.Find(itemId);
if (sepIndex != m_Separators.InvalidIndex())
{
itemHeight += MENU_SEPARATOR_HEIGHT;
}
if (tall + itemHeight > nNumPixelsAvailable)
break;
// Too many items
if (maxVisibleItems > 0)
{
if (m_VisibleSortedItems.Count() >= maxVisibleItems)
break;
}
tall += itemHeight;
// Re-attach this one
m_VisibleSortedItems.AddToTail(itemId);
++startItem;
} while (true);
}
//-----------------------------------------------------------------------------
// Purpose: Get the approproate menu border
//-----------------------------------------------------------------------------
void Menu::LayoutMenuBorder()
{
IBorder *menuBorder;
IScheme *pScheme = scheme()->GetIScheme(GetScheme());
menuBorder = pScheme->GetBorder("MenuBorder");
if (menuBorder)
{
SetBorder(menuBorder);
}
}
//-----------------------------------------------------------------------------
// Purpose: Draw a black border on the right side of the menu items
//-----------------------------------------------------------------------------
void Menu::Paint()
{
if (m_pScroller->IsVisible())
{
// draw black bar
int wide, tall;
GetSize(wide, tall);
surface()->DrawSetColor(_borderDark);
if (IsProportional())
{
surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
}
else
{
surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: sets the max number of items visible (scrollbar appears with more)
// Input : numItems -
//-----------------------------------------------------------------------------
void Menu::SetNumberOfVisibleItems(int numItems)
{
m_iNumVisibleLines = numItems;
InvalidateLayout(false);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
MenuItem *Menu::GetMenuItem(int itemID)
{
if (!m_MenuItems.IsValidIndex(itemID))
return NULL;
return m_MenuItems[itemID];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool Menu::IsValidMenuID(int itemID)
{
return m_MenuItems.IsValidIndex(itemID);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Menu::GetInvalidMenuID()
{
return m_MenuItems.InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: When a menuItem is selected, close cascading menus
// if the menuItem selected has a cascading menu attached, we
// want to keep that one open so skip it.
// Passing NULL will close all cascading menus.
//-----------------------------------------------------------------------------
void Menu::CloseOtherMenus(MenuItem *item)
{
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i] == item)
continue;
m_MenuItems[i]->CloseCascadeMenu();
}
}
//-----------------------------------------------------------------------------
// Purpose: Respond to string commands.
//-----------------------------------------------------------------------------
void Menu::OnCommand(const char *command)
{
// forward on the message
PostActionSignal(new KeyValues("Command", "command", command));
Panel::OnCommand(command);
}
//-----------------------------------------------------------------------------
// Purpose: Handle key presses, Activate shortcuts
//-----------------------------------------------------------------------------
void Menu::OnKeyCodeTyped(KeyCode code)
{
// Don't allow key inputs when disabled!
if (!IsEnabled())
return;
bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
if (alt)
{
BaseClass::OnKeyCodeTyped(code);
PostActionSignal(new KeyValues("MenuClose"));
}
switch (code)
{
case KEY_ESCAPE:
{
// hide the menu on ESC
SetVisible(false);
break;
}
// arrow keys scroll through items on the list.
// they should also scroll the scroll bar if needed
case KEY_UP:
{
MoveAlongMenuItemList(MENU_UP, 0);
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
}
break;
}
case KEY_DOWN:
{
MoveAlongMenuItemList(MENU_DOWN, 0);
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
}
break;
}
// for now left and right arrows just open or close submenus if they are there.
case KEY_RIGHT:
{
// make sure a menuItem is currently selected
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
if (m_MenuItems[m_iCurrentlySelectedItemID]->HasMenu())
{
ActivateItem(m_iCurrentlySelectedItemID);
}
else
{
BaseClass::OnKeyCodeTyped(code);
}
}
else
{
BaseClass::OnKeyCodeTyped(code);
}
break;
}
case KEY_LEFT:
{
// if our parent is a menu item then we are a submenu so close us.
if (GetParentMenuItem())
{
SetVisible(false);
}
else
{
BaseClass::OnKeyCodeTyped(code);
}
break;
}
case KEY_ENTER:
{
// make sure a menuItem is currently selected
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
ActivateItem(m_iCurrentlySelectedItemID);
}
else
{
BaseClass::OnKeyCodeTyped(code); // chain up
}
break;
}
}
// don't chain back
}
//-----------------------------------------------------------------------------
// Purpose: Handle key presses, Activate shortcuts
// Input : code -
//-----------------------------------------------------------------------------
void Menu::OnKeyTyped(wchar_t unichar)
{
//
// NOTE - if hotkeys are ever enabled you need to work out a way to differentiate between
// combo box menus (which can't have hot keys) and system style menus (which do have hot keys).
//
//
/* if (unichar)
{
// iterate the menu items looking for one with the matching hotkey
FOR_EACH_LL( m_MenuItems, i )
{
MenuItem *panel = m_MenuItems[i];
if (panel->IsVisible())
{
Panel *hot = panel->HasHotkey(unichar);
if (hot)
{
// post a message to the menuitem telling it it's hotkey was pressed
PostMessage(hot, new KeyValues("Hotkey"));
return;
}
// if the menuitem is a cascading menuitem and it is open, check its hotkeys too
Menu *cascadingMenu = panel->GetMenu();
if (cascadingMenu && cascadingMenu->IsVisible())
{
cascadingMenu->OnKeyTyped(unichar);
}
}
}
}
*/
int itemToSelect = m_iCurrentlySelectedItemID;
if (itemToSelect < 0)
{
itemToSelect = 0;
}
int i;
wchar_t menuItemName[255];
i = itemToSelect + 1;
if (i >= m_MenuItems.Count())
{
i = 0;
}
while (i != itemToSelect)
{
m_MenuItems[i]->GetText(menuItemName, 254);
if (towlower(menuItemName[0]) == towlower(unichar))
{
itemToSelect = i;
break;
}
i++;
if (i >= m_MenuItems.Count())
{
i = 0;
}
}
if (itemToSelect >= 0)
{
SetCurrentlyHighlightedItem(itemToSelect);
InvalidateLayout();
}
// don't chain back
}
//-----------------------------------------------------------------------------
// Purpose: Handle the mouse wheel event, scroll the selection
//-----------------------------------------------------------------------------
void Menu::OnMouseWheeled(int delta)
{
if (!m_pScroller->IsVisible())
return;
int val = m_pScroller->GetValue();
val -= delta;
m_pScroller->SetValue(val);
// moving the slider redraws the scrollbar,
// and so we should redraw the menu since the
// menu draws the black border to the right of the scrollbar.
InvalidateLayout();
// don't chain back
}
//-----------------------------------------------------------------------------
// Purpose: Lose focus, hide menu
//-----------------------------------------------------------------------------
void Menu::OnKillFocus()
{
// check to see if it's a child taking it
if (!input()->GetFocus() || !ipanel()->HasParent(input()->GetFocus(), GetVPanel()))
{
// if we don't accept keyboard input, then we have to ignore the killfocus if it's not actually being stolen
if (!IsKeyBoardInputEnabled() && !input()->GetFocus())
return;
// get the parent of this menu.
MenuItem *item = GetParentMenuItem();
// if the parent is a menu item, this menu is a cascading menu
// if the panel that is getting focus is the parent menu, don't close this menu.
if ((item) && (input()->GetFocus() == item->GetVParent()))
{
// if we are in mouse mode and we clicked on the menuitem that
// triggers the cascading menu, leave it open.
if (m_iInputMode == MOUSE)
{
// return the focus to the cascading menu.
MoveToFront();
return;
}
}
// forward the message to the parent.
PostActionSignal(new KeyValues("MenuClose"));
// hide this menu
SetVisible(false);
}
}
namespace vgui2
{
class CMenuManager
{
public:
void AddMenu(Menu *m)
{
if (!m)
return;
int c = m_Menus.Count();
for (int i = 0; i < c; ++i)
{
if (m_Menus[i].Get() == m)
return;
}
DHANDLE< Menu > h;
h = m;
m_Menus.AddToTail(h);
}
void RemoveMenu(Menu *m)
{
if (!m)
return;
int c = m_Menus.Count();
for (int i = c - 1; i >= 0; --i)
{
if (m_Menus[i].Get() == m)
{
m_Menus.Remove(i);
return;
}
}
}
void OnInternalMousePressed(Panel *other, MouseCode code)
{
int c = m_Menus.Count();
if (!c)
return;
int x, y;
input()->GetCursorPos(x, y);
bool mouseInsideMenuRelatedPanel = false;
for (int i = c - 1; i >= 0; --i)
{
Menu *m = m_Menus[i].Get();
if (!m)
{
m_Menus.Remove(i);
continue;
}
// See if the mouse is within a menu
if (IsWithinMenuOrRelative(m, x, y))
{
mouseInsideMenuRelatedPanel = true;
}
}
if (mouseInsideMenuRelatedPanel)
{
return;
}
AbortMenus();
}
void AbortMenus()
{
// Close all of the menus
int c = m_Menus.Count();
for (int i = c - 1; i >= 0; --i)
{
Menu *m = m_Menus[i].Get();
if (!m)
{
continue;
}
m_Menus.Remove(i);
// Force it to close
m->SetVisible(false);
}
m_Menus.RemoveAll();
}
bool IsWithinMenuOrRelative(Panel *panel, int x, int y)
{
VPANEL topMost = panel->IsWithinTraverse(x, y, true);
if (topMost)
{
// It's over the menu
if (topMost == panel->GetVPanel())
{
return true;
}
// It's over something which is parented to the menu (i.e., a menu item)
if (ipanel()->HasParent(topMost, panel->GetVPanel()))
{
return true;
}
}
if (panel->GetParent())
{
Panel *parent = panel->GetParent();
topMost = parent->IsWithinTraverse(x, y, true);
if (topMost)
{
if (topMost == parent->GetVPanel())
{
return true;
}
/*
// NOTE: this check used to not cast to MenuButton, but it seems wrong to me
// since if the mouse is over another child of the parent panel to the menu then
// the menu stays visible. I think this is bogus.
Panel *pTopMost = ipanel()->GetPanel(topMost, GetControlsModuleName());
if ( pTopMost &&
ipanel()->HasParent( topMost, parent->GetVPanel() ) &&
dynamic_cast< MenuButton * >( pTopMost ) )
{
Msg( "topMost %s has parent %s\n",
ipanel()->GetName( topMost ),
parent->GetName() );
return true;
}
*/
}
}
return false;
}
#ifdef DBGFLAG_VALIDATE
void Validate(CValidator &validator, char *pchName)
{
validator.Push("CMenuManager", this, pchName);
m_Menus.Validate(validator, "m_Menus");
validator.Pop();
}
#endif
private:
// List of visible menus
CUtlVector< DHANDLE< Menu > > m_Menus;
};
// Singleton helper class
static CMenuManager g_MenuMgr;
void ValidateMenuGlobals(CValidator &validator)
{
#ifdef DBGFLAG_VALIDATE
g_MenuMgr.Validate(validator, "g_MenuMgr");
#endif
}
} // end namespace vgui
//-----------------------------------------------------------------------------
// Purpose: Static method called on mouse released to see if Menu objects should be aborted
// Input : *other -
// code -
//-----------------------------------------------------------------------------
void Menu::OnInternalMousePressed(Panel *other, MouseCode code)
{
g_MenuMgr.OnInternalMousePressed(other, code);
}
//-----------------------------------------------------------------------------
// Purpose: Set visibility of menu and its children as appropriate.
//-----------------------------------------------------------------------------
void Menu::SetVisible(bool state)
{
if (state == IsVisible())
return;
if (state == false)
{
PostActionSignal(new KeyValues("MenuClose"));
CloseOtherMenus(NULL);
SetCurrentlySelectedItem(-1);
g_MenuMgr.RemoveMenu(this);
}
else if (state == true)
{
MoveToFront();
RequestFocus();
g_MenuMgr.AddMenu(this);
}
// must be after movetofront()
BaseClass::SetVisible(state);
_sizedForScrollBar = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Menu::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
SetFgColor(GetSchemeColor("Menu.TextColor", GetSchemeColor("Menu/FgColor", Color(255, 255, 255, 155), pScheme), pScheme));
SetBgColor(GetSchemeColor("Menu.BgColor", GetSchemeColor("Menu/BgColor", Color(255, 255, 255, 155), pScheme), pScheme));
_borderDark = GetSchemeColor("Border.Dark", GetSchemeColor("BorderDark", Color(255, 255, 255, 0), pScheme), pScheme);
const char *resourceString = pScheme->GetResourceString("Menu/TopLeft");
if (resourceString[0])
{
m_bImageBackground = true;
m_pTopBackground[0] = scheme()->GetImage(resourceString, true);
m_pTopBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/TopCenter"), true);
m_pTopBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/TopRight"), true);
m_pCenterBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleLeft"), true);
m_pCenterBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleCenter"), true);
m_pCenterBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleRight"), true);
m_pBottomBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomLeft"), true);
m_pBottomBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomCenter"), true);
m_pBottomBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomRight"), true);
}
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i]->IsCheckable())
{
int wide, tall;
m_MenuItems[i]->GetCheckImageSize(wide, tall);
m_iCheckImageWidth = max(m_iCheckImageWidth, wide);
}
}
_recalculateWidth = true;
CalculateWidth();
InvalidateLayout();
}
void Menu::SetBgColor(Color newColor)
{
BaseClass::SetBgColor(newColor);
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i]->HasMenu())
{
m_MenuItems[i]->GetMenu()->SetBgColor(newColor);
}
}
}
void Menu::SetFgColor(Color newColor)
{
BaseClass::SetFgColor(newColor);
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i]->HasMenu())
{
m_MenuItems[i]->GetMenu()->SetFgColor(newColor);
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Menu::SetBorder(class IBorder *border)
{
Panel::SetBorder(border);
}
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to a MenuItem that is this menus parent, if it has one
//-----------------------------------------------------------------------------
MenuItem *Menu::GetParentMenuItem()
{
return dynamic_cast<MenuItem *>(GetParent());
}
//-----------------------------------------------------------------------------
// Purpose: Hide the menu when an item has been selected
//-----------------------------------------------------------------------------
void Menu::OnMenuItemSelected(Panel *panel)
{
SetVisible(false);
m_pScroller->SetVisible(false);
// chain this message up through the hierarchy so
// all the parent menus will close
// get the parent of this menu.
MenuItem *item = GetParentMenuItem();
// if the parent is a menu item, this menu is a cascading menu
if (item)
{
// get the parent of the menuitem. it should be a menu.
Menu *parentMenu = item->GetParentMenu();
if (parentMenu)
{
// send the message to this parent menu
KeyValues *kv = new KeyValues("MenuItemSelected");
kv->SetPtr("panel", panel);
ivgui()->PostMessage(parentMenu->GetVPanel(), kv, GetVPanel());
}
}
bool activeItemSet = false;
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i] == panel)
{
activeItemSet = true;
m_iActivatedItem = i;
break;
}
}
if (!activeItemSet)
{
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i]->HasMenu())
{
/*
// GetActiveItem needs to return -1 or similar if it hasn't been set...
if( m_MenuItems[i]->GetActiveItem() )
{
m_iActivatedItem = m_MenuItems[i]->GetActiveItem();
}*/
}
}
}
// also pass it to the parent so they can respond if they like
if (GetVParent())
{
KeyValues *kv = new KeyValues("MenuItemSelected");
kv->SetPtr("panel", panel);
ivgui()->PostMessage(GetVParent(), kv, GetVPanel());
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Menu::GetActiveItem()
{
return m_iActivatedItem;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
KeyValues *Menu::GetItemUserData(int itemID)
{
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
// make sure its enabled since disabled items get highlighted.
if (menuItem && menuItem->IsEnabled())
{
return menuItem->GetUserData();
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void Menu::GetItemText(int itemID, wchar_t *text, int bufLenInBytes)
{
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
if (menuItem)
{
menuItem->GetText(text, bufLenInBytes);
return;
}
}
text[0] = 0;
}
void Menu::GetItemText(int itemID, char *text, int bufLenInBytes)
{
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
if (menuItem)
{
menuItem->GetText(text, bufLenInBytes);
return;
}
}
text[0] = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Activate the n'th item in the menu list, as if that menu item had been selected by the user
//-----------------------------------------------------------------------------
void Menu::ActivateItem(int itemID)
{
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
// make sure its enabled since disabled items get highlighted.
if (menuItem && menuItem->IsEnabled())
{
menuItem->FireActionSignal();
m_iActivatedItem = itemID;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Menu::ActivateItemByRow(int row)
{
if (m_SortedItems.IsValidIndex(row))
{
ActivateItem(m_SortedItems[row]);
}
}
//-----------------------------------------------------------------------------
// Purpose: Return the number of items currently in the menu list
//-----------------------------------------------------------------------------
int Menu::GetItemCount()
{
return m_MenuItems.Count();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Menu::GetMenuID(int index)
{
if (!m_SortedItems.IsValidIndex(index))
return m_MenuItems.InvalidIndex();
return m_SortedItems[index];
}
//-----------------------------------------------------------------------------
// Purpose: Return the number of items currently visible in the menu list
//-----------------------------------------------------------------------------
int Menu::GetCurrentlyVisibleItemsCount()
{
if (m_MenuItems.Count() < m_iNumVisibleLines)
{
int cMenuItems = 0;
FOR_EACH_LL(m_MenuItems, i)
{
if (m_MenuItems[i]->IsVisible())
{
++cMenuItems;
}
}
return cMenuItems;
}
return m_iNumVisibleLines;
}
//-----------------------------------------------------------------------------
// Purpose: Enables/disables choices in the list
// itemText - string name of item in the list
// state - true enables, false disables
//-----------------------------------------------------------------------------
void Menu::SetItemEnabled(const char *itemName, bool state)
{
FOR_EACH_LL(m_MenuItems, i)
{
if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
{
m_MenuItems[i]->SetEnabled(state);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Enables/disables choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemEnabled(int itemID, bool state)
{
if (!m_MenuItems.IsValidIndex(itemID))
return;
m_MenuItems[itemID]->SetEnabled(state);
}
//-----------------------------------------------------------------------------
// Purpose: shows/hides choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemVisible(const char *itemName, bool state)
{
FOR_EACH_LL(m_MenuItems, i)
{
if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
{
m_MenuItems[i]->SetVisible(state);
InvalidateLayout();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: shows/hides choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemVisible(int itemID, bool state)
{
if (!m_MenuItems.IsValidIndex(itemID))
return;
m_MenuItems[itemID]->SetVisible(state);
}
//-----------------------------------------------------------------------------
// Purpose: Make the scroll bar visible and narrow the menu
// also make items visible or invisible in the list as appropriate
//-----------------------------------------------------------------------------
void Menu::AddScrollBar()
{
m_pScroller->SetVisible(true);
_sizedForScrollBar = true;
}
//-----------------------------------------------------------------------------
// Purpose: Make the scroll bar invisible and widen the menu
//-----------------------------------------------------------------------------
void Menu::RemoveScrollBar()
{
m_pScroller->SetVisible(false);
_sizedForScrollBar = false;
}
//-----------------------------------------------------------------------------
// Purpose: Invalidate layout if the slider is moved so items scroll
//-----------------------------------------------------------------------------
void Menu::OnSliderMoved()
{
CloseOtherMenus(NULL); // close any cascading menus
// Invalidate so we redraw the menu!
InvalidateLayout();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Toggle into mouse mode.
//-----------------------------------------------------------------------------
void Menu::OnCursorMoved(int x, int y)
{
m_iInputMode = MOUSE;
// chain up
CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
RequestFocus();
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Toggle into keyboard mode.
//-----------------------------------------------------------------------------
void Menu::OnKeyCodePressed(KeyCode code)
{
m_iInputMode = KEYBOARD;
// send the message to this parent in case this is a cascading menu
if (GetVParent())
{
ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the item currently highlighted in the menu by ptr
//-----------------------------------------------------------------------------
void Menu::SetCurrentlySelectedItem(MenuItem *item)
{
int itemNum = -1;
// find it in our list of menuitems
FOR_EACH_LL(m_MenuItems, i)
{
MenuItem *child = m_MenuItems[i];
if (child == item)
{
itemNum = i;
break;
}
}
Assert(itemNum >= 0);
SetCurrentlySelectedItem(itemNum);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Menu::ClearCurrentlyHighlightedItem()
{
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
}
m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the item currently highlighted in the menu by index
//-----------------------------------------------------------------------------
void Menu::SetCurrentlySelectedItem(int itemID)
{
// dont deselect if its the same item
if (itemID == m_iCurrentlySelectedItemID)
return;
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
}
PostActionSignal(new KeyValues("MenuItemHighlight", "itemID", itemID));
m_iCurrentlySelectedItemID = itemID;
}
//-----------------------------------------------------------------------------
// This will set the item to be currenly selected and highlight it
// will not open cascading menu. This was added for comboboxes
// to have the combobox item highlighted in the menu when they open the
// dropdown.
//-----------------------------------------------------------------------------
void Menu::SetCurrentlyHighlightedItem(int itemID)
{
SetCurrentlySelectedItem(itemID);
int row = m_SortedItems.Find(itemID);
Assert(row != -1);
// if there is a scroll bar, and we scroll off lets move it.
if (m_pScroller->IsVisible())
{
// now if we are off the scroll bar, it means we moved the scroll bar
// by hand or set the item off the list
// so just snap the scroll bar straight to the item.
if ((row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) ||
(row < m_pScroller->GetValue()))
{
if (!m_pScroller->IsVisible())
return;
m_pScroller->SetValue(row);
}
}
if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
{
if (!m_MenuItems[m_iCurrentlySelectedItemID]->IsArmed())
{
m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Menu::GetCurrentlyHighlightedItem()
{
return m_iCurrentlySelectedItemID;
}
//-----------------------------------------------------------------------------
// Purpose: Respond to cursor entering a menuItem.
//-----------------------------------------------------------------------------
void Menu::OnCursorEnteredMenuItem(int VPanel)
{
VPANEL menuItem = (VPANEL)VPanel;
// if we are in mouse mode
if (m_iInputMode == MOUSE)
{
MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
// arm the menu
item->ArmItem();
// open the cascading menu if there is one.
item->OpenCascadeMenu();
SetCurrentlySelectedItem(item);
}
}
//-----------------------------------------------------------------------------
// Purpose: Respond to cursor exiting a menuItem
//-----------------------------------------------------------------------------
void Menu::OnCursorExitedMenuItem(int VPanel)
{
VPANEL menuItem = (VPANEL)VPanel;
// only care if we are in mouse mode
if (m_iInputMode == MOUSE)
{
MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
// unhighlight the item.
// note menuItems with cascading menus will stay lit.
item->DisarmItem();
}
}
//-----------------------------------------------------------------------------
// Purpose: Move up or down one in the list of items in the menu
// Direction is MENU_UP or MENU_DOWN
//-----------------------------------------------------------------------------
void Menu::MoveAlongMenuItemList(int direction, int loopCount)
{
int itemID = m_iCurrentlySelectedItemID;
int row = m_SortedItems.Find(itemID);
row += direction;
if (row > m_SortedItems.Count() - 1)
{
if (m_pScroller->IsVisible())
{
// stop at bottom of scrolled list
row = m_SortedItems.Count() - 1;
}
else
{
// if no scroll bar we circle around
row = 0;
}
}
else if (row < 0)
{
if (m_pScroller->IsVisible())
{
// stop at top of scrolled list
row = m_pScroller->GetValue();
}
else
{
// if no scroll bar circle around
row = m_SortedItems.Count() - 1;
}
}
// if there is a scroll bar, and we scroll off lets move it.
if (m_pScroller->IsVisible())
{
if (row > m_pScroller->GetValue() + m_iNumVisibleLines - 1)
{
int val = m_pScroller->GetValue();
val -= -direction;
m_pScroller->SetValue(val);
// moving the slider redraws the scrollbar,
// and so we should redraw the menu since the
// menu draws the black border to the right of the scrollbar.
InvalidateLayout();
}
else if (row < m_pScroller->GetValue())
{
int val = m_pScroller->GetValue();
val -= -direction;
m_pScroller->SetValue(val);
// moving the slider redraws the scrollbar,
// and so we should redraw the menu since the
// menu draws the black border to the right of the scrollbar.
InvalidateLayout();
}
// now if we are still off the scroll bar, it means we moved the scroll bar
// by hand and created a situation in which we moved an item down, but the
// scroll bar is already too far down and should scroll up or vice versa
// so just snap the scroll bar straight to the item.
if ((row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) ||
(row < m_pScroller->GetValue()))
{
m_pScroller->SetValue(row);
}
}
// switch it back to an itemID from row
if (m_SortedItems.IsValidIndex(row))
{
SetCurrentlySelectedItem(m_SortedItems[row]);
}
// don't allow us to loop around more than once
if (loopCount < m_MenuItems.Count())
{
// see if the text is empty, if so skip
wchar_t text[256];
m_MenuItems[m_iCurrentlySelectedItemID]->GetText(text, 255);
if (text[0] == 0 || !m_MenuItems[m_iCurrentlySelectedItemID]->IsVisible())
{
// menu item is empty, keep moving along
MoveAlongMenuItemList(direction, loopCount + 1);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Return which type of events the menu is currently interested in
// MenuItems need to know because behaviour is different depending on mode.
//-----------------------------------------------------------------------------
int Menu::GetMenuMode()
{
return m_iInputMode;
}
//-----------------------------------------------------------------------------
// Purpose: Set the menu to key mode if a child menu goes into keymode
// This mode change has to be chained up through the menu heirarchy
// so cascading menus will work when you do a bunch of stuff in keymode
// in high level menus and then switch to keymode in lower level menus.
//-----------------------------------------------------------------------------
void Menu::OnKeyModeSet()
{
m_iInputMode = KEYBOARD;
}
//-----------------------------------------------------------------------------
// Purpose: Set the checked state of a menuItem
//-----------------------------------------------------------------------------
void Menu::SetMenuItemChecked(int itemID, bool state)
{
m_MenuItems[itemID]->SetChecked(state);
}
//-----------------------------------------------------------------------------
// Purpose: Check if item is checked.
//-----------------------------------------------------------------------------
bool Menu::IsChecked(int itemID)
{
return m_MenuItems[itemID]->IsChecked();
}
//-----------------------------------------------------------------------------
// Purpose: Set the minmum width the menu has to be. This
// is useful if you have a menu that is sized to the largest item in it
// but you don't want the menu to be thinner than the menu button
//-----------------------------------------------------------------------------
void Menu::SetMinimumWidth(int width)
{
m_iMinimumWidth = width;
}
//-----------------------------------------------------------------------------
// Purpose: Get the minmum width the menu
//-----------------------------------------------------------------------------
int Menu::GetMinimumWidth()
{
return m_iMinimumWidth;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
//-----------------------------------------------------------------------------
void Menu::AddSeparator()
{
int lastID = m_MenuItems.Count() - 1;
m_Separators.AddToTail(lastID);
m_SeparatorPanels.AddToTail(new MenuSeparator(this, "MenuSeparator"));
}
void Menu::AddSeparatorAfterItem(int itemID)
{
Assert(m_MenuItems.IsValidIndex(itemID));
m_Separators.AddToTail(itemID);
m_SeparatorPanels.AddToTail(new MenuSeparator(this, "MenuSeparator"));
}
void Menu::MoveMenuItem(int itemID, int moveBeforeThisItemID)
{
int c = m_SortedItems.Count();
int i;
for (i = 0; i < c; ++i)
{
if (m_SortedItems[i] == itemID)
{
m_SortedItems.Remove(i);
break;
}
}
// Didn't find it
if (i >= c)
{
return;
}
// Now find insert pos
c = m_SortedItems.Count();
for (i = 0; i < c; ++i)
{
if (m_SortedItems[i] == moveBeforeThisItemID)
{
m_SortedItems.InsertBefore(i, itemID);
break;
}
}
}
void Menu::SetFont(HFont font)
{
m_hItemFont = font;
if (font)
{
m_iMenuItemHeight = surface()->GetFontTall(font) + 2;
}
InvalidateLayout();
}
void Menu::SetCurrentKeyBinding(int itemID, char const *hotkey)
{
if (m_MenuItems.IsValidIndex(itemID))
{
MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
menuItem->SetCurrentKeyBinding(hotkey);
}
}
//-----------------------------------------------------------------------------
// Purpose: Static method to display a context menu
// Input : *parent -
// *menu -
//-----------------------------------------------------------------------------
void Menu::PlaceContextMenu(Panel *parent, Menu *menu)
{
Assert(parent);
Assert(menu);
if (!menu || !parent)
return;
menu->SetVisible(false);
menu->SetParent(parent);
menu->AddActionSignalTarget(parent);
// get cursor position, this is local to this text edit window
int cursorX, cursorY;
input()->GetCursorPos(cursorX, cursorY);
menu->SetVisible(true);
// relayout the menu immediately so that we know it's size
menu->InvalidateLayout(true);
int menuWide, menuTall;
menu->GetSize(menuWide, menuTall);
// work out where the cursor is and therefore the best place to put the menu
int wide, tall;
surface()->GetScreenSize(wide, tall);
if (wide - menuWide > cursorX)
{
// menu hanging right
if (tall - menuTall > cursorY)
{
// menu hanging down
menu->SetPos(cursorX, cursorY);
}
else
{
// menu hanging up
menu->SetPos(cursorX, cursorY - menuTall);
}
}
else
{
// menu hanging left
if (tall - menuTall > cursorY)
{
// menu hanging down
menu->SetPos(cursorX - menuWide, cursorY);
}
else
{
// menu hanging up
menu->SetPos(cursorX - menuWide, cursorY - menuTall);
}
}
menu->RequestFocus();
}
void Menu::SetUseFallbackFont(bool bState, HFont hFallback)
{
m_hFallbackItemFont = hFallback;
m_bUseFallbackFont = bState;
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
// allocations.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void Menu::Validate(CValidator &validator, char *pchName)
{
validator.Push("vgui2::Menu", this, pchName);
m_MenuItems.Validate(validator, "m_MenuItems");
m_SortedItems.Validate(validator, "m_SortedItems");
BaseClass::Validate(validator, "vgui2::Menu");
validator.Pop();
}
#endif // DBGFLAG_VALIDATE
void Menu::PaintBackground()
{
if (!m_bImageBackground)
return BaseClass::PaintBackground();
int wide, tall;
GetSize(wide, tall);
const int iOffset = 0;
m_pTopBackground[0]->SetPos(0, 0);
m_pTopBackground[0]->SetSize(7, 7);
m_pTopBackground[0]->Paint();
m_pTopBackground[1]->SetPos(7, 0);
m_pTopBackground[1]->SetSize(wide - 14 - iOffset, 7);
m_pTopBackground[1]->Paint();
m_pTopBackground[2]->SetPos(wide - 7 - iOffset, 0);
m_pTopBackground[2]->SetSize(7, 7);
m_pTopBackground[2]->Paint();
m_pCenterBackground[0]->SetPos(0, 7);
m_pCenterBackground[0]->SetSize(7, tall - 14);
m_pCenterBackground[0]->Paint();
m_pCenterBackground[1]->SetPos(7, 7);
m_pCenterBackground[1]->SetSize(wide - 14 - iOffset, tall - 14);
m_pCenterBackground[1]->Paint();
m_pCenterBackground[2]->SetPos(wide - 7 - iOffset, 7);
m_pCenterBackground[2]->SetSize(7, tall - 14);
m_pCenterBackground[2]->Paint();
m_pBottomBackground[0]->SetPos(0, tall - 7);
m_pBottomBackground[0]->SetSize(7, 7);
m_pBottomBackground[0]->Paint();
m_pBottomBackground[1]->SetPos(7, tall - 7);
m_pBottomBackground[1]->SetSize(wide - 14 - iOffset, 7);
m_pBottomBackground[1]->Paint();
m_pBottomBackground[2]->SetPos(wide - 7, tall - 7);
m_pBottomBackground[2]->SetSize(7, 7);
m_pBottomBackground[2]->Paint();
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/yoyojacky/CSMoE.git
git@gitee.com:yoyojacky/CSMoE.git
yoyojacky
CSMoE
CSMoE
master

搜索帮助