source-engine/public/vgui_controls/Menu.h
2022-06-05 01:13:13 +03:00

381 lines
15 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#ifndef MENU_H
#define MENU_H
#ifdef _WIN32
#pragma once
#endif
#include <vgui_controls/Panel.h>
#include <vgui_controls/Label.h>
#include <utllinkedlist.h>
#include <utlvector.h>
namespace vgui
{
class MenuItem;
class ScrollBar;
class MenuSeparator;
//-----------------------------------------------------------------------------
// Purpose: A menu is a list of items that can be selected with one click, navigated
// with arrow keys and/or hot keys, and have a lit behavior when mouse over.
// It is NOT the button which opens the menu, but only the menu itself.
//
// Behaviour spec:
// Menu navigation can be done in 2 modes, via keyboard keys and via mouse.
// Clicking on menu button opens menu.
// Only one item in a menu is highlighted at a time.
// Only one submenu in a menu is open at a time.
// Disabled menuitems get highlighted via mouse and keys but will not activate.
//
// Mouse:
// Moving mouse into a menuitem highlights it.
// If the menuitem has a cascading menu, the menu opens when the mouse enters
// the menuitem. The cascading menuitem stays highlighted while its menu is open.
// No submenu items are highlighted by default.
// Moving the mouse into another menuitem closes any previously open submenus in the list.
// Clicking once in the menu item activates the menu item and closes all menus.
// Moving the mouse off a menuitem unhighlights it.
// The scroll bar arrows can be used to move up/down the menu one item at a time.
// The clicking and dragging on the scroll bar nob also scrolls the menu items.
// If a highlighed menuitem scrolls off, and the user then begins navigating via keys,
// the menu will snap the scroll bar so the highlighted item is visible.
// If user has been navigating via keys, moving the mouse over a menu item
// highlights it.
// Mousewheel:
// You must have the mouse inside the menu/scroll bar to use the wheel.
// The mouse wheel moves the highlighted menuitem up or down the list.
// If the list has no scroll bar the wheel will cycle from the bottom of the list
// to the top of the list and vice versa.
// If the list has a scrollbar the mouse wheel will stop at the top or bottom
// of the list.
// If the mouse is over the scroll bar no items are highlighted.
// Keyboard:
// When a menu is opened, no items are highlighted.
// If a menuitem has a cascading menu it does not open when the item is highlighted.
// The down arrow selects the next item in the list.
// (first item if none are highlighted and there is a scrollbar).
// The up arrow selects the previous item in the list
// (first item if none are highlighted and there is a scrollbar, last item if none are
// highlighted and there is no scrollbar).
// Selecting a new menuitem closes any previously open submenus in the list.
// The enter key activates the selected item and closes all menus.
// If the selected item has a cascading menu, activating it opens its submenu.
// These may also be activated by pressing the right arrow.
// Pressing the left arrow closes the submenu.
// When the submenu is opened the cascading menuitem stays highlighted.
// No items in the submenu are highlighted when it is opened.
//
// Note: Cascading menuitems in menus with a scrollbar is not supported.
// Its a clunky UI and if we want this we should design a better solution,
// perhaps along the lines of how explorer's bookmarks does it.
// It currently functions, but there are some arm/disarm bugs.
//
//
//-----------------------------------------------------------------------------
class Menu : public Panel
{
DECLARE_CLASS_SIMPLE( Menu, Panel );
friend class MenuItem;
public:
enum MenuDirection_e
{
LEFT,
RIGHT,
UP,
DOWN,
CURSOR, // make the menu appear under the mouse cursor
ALIGN_WITH_PARENT, // make the menu appear under the parent
};
Menu(Panel *parent, const char *panelName);
~Menu();
static void PlaceContextMenu( Panel *parent, Menu *menu );
static void OnInternalMousePressed( Panel *other, MouseCode code );
virtual void PositionRelativeToPanel( Panel *reference, MenuDirection_e direction, int nAdditionalYOffset = 0, bool showMenu = false );
// the menu. For combo boxes, it's the edit/field, etc. etc.
// Add a simple text item to the menu
virtual int AddMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData = NULL );
virtual int AddMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData = NULL );
virtual int AddMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target , const KeyValues *userData = NULL);
virtual int AddMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target , const KeyValues *userData = NULL);
virtual int AddMenuItem( const char *itemText, const char *command, Panel *target , const KeyValues *userData = NULL);
virtual int AddMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL );
virtual int AddMenuItem( const char *itemText, Panel *target, const KeyValues *userData = NULL );
// Add a checkable item to the menu
virtual int AddCheckableMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData = NULL );
virtual int AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData = NULL );
virtual int AddCheckableMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL );
virtual int AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL );
virtual int AddCheckableMenuItem( const char *itemText, const char *command, Panel *target , const KeyValues *userData = NULL);
virtual int AddCheckableMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL );
virtual int AddCheckableMenuItem( const char *itemText, Panel *target, const KeyValues *userData = NULL );
// Add a cascading menu item to the menu
virtual int AddCascadingMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
virtual int AddCascadingMenuItem( const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL );
// Add a custom panel to the menu
virtual int AddMenuItem( MenuItem *panel );
virtual void AddSeparator();
virtual void AddSeparatorAfterItem( int itemID );
// Sets the values of a menu item at the specified index
virtual void UpdateMenuItem(int itemID, const char *itemText,KeyValues *message, const KeyValues *userData = NULL);
virtual void UpdateMenuItem(int itemID, const wchar_t *wszItemText,KeyValues *message, const KeyValues *userData = NULL);
virtual void MoveMenuItem( int itemID, int moveBeforeThisItemID );
virtual bool IsValidMenuID(int itemID);
virtual int GetInvalidMenuID();
KeyValues *GetItemUserData(int itemID);
void GetItemText(int itemID, wchar_t *text, int bufLenInBytes);
void GetItemText(int itemID, char *text, int bufLenInBytes);
virtual void SetItemEnabled(const char *itemName, bool state);
virtual void SetItemEnabled(int itemID, bool state);
virtual void SetItemVisible(const char *itemName, bool visible);
virtual void SetItemVisible(int itemID, bool visible);
// Remove a single item
void DeleteItem( int itemID );
// Clear the menu, deleting all the menu items within
void DeleteAllItems();
// Override the auto-width setting with a single fixed width
virtual void SetFixedWidth( int width );
// Sets the content alignment of all items in the menu
void SetContentAlignment( Label::Alignment alignment );
// sets the height of each menu item
virtual void SetMenuItemHeight(int itemHeight);
virtual int GetMenuItemHeight() const;
// Set the max number of items visible (scrollbar appears with more)
virtual void SetNumberOfVisibleItems( int numItems );
// Add the menu to the menu manager (see Menu::SetVisible())?
void EnableUseMenuManager( bool bUseMenuManager );
// Set up the menu items layout
virtual void PerformLayout( void );
virtual void SetBorder(class IBorder *border);
virtual void ApplySchemeSettings(IScheme *pScheme);
// Set type ahead behaviour
enum MenuTypeAheadMode
{
COMPAT_MODE = 0,
HOT_KEY_MODE,
TYPE_AHEAD_MODE,
};
virtual void SetTypeAheadMode(MenuTypeAheadMode mode);
virtual int GetTypeAheadMode();
// Hotkey handling
virtual void OnKeyTyped(wchar_t unichar);
// Menu nagivation etc.
virtual void OnKeyCodeTyped( KeyCode code );
// Visibility
virtual void SetVisible(bool state);
// Activates item in the menu list, as if that menu item had been selected by the user
virtual void ActivateItem(int itemID);
virtual void SilentActivateItem(int itemID); // activate item, but don't fire the action signal
virtual void ActivateItemByRow(int row);
virtual int GetActiveItem(); // returns the itemID (not the row) of the active item
// Return the number of items currently in the menu list
virtual int GetItemCount() const;
// return the menuID of the n'th item in the menu list, valid from [0, GetItemCount)
virtual int GetMenuID(int index);
// Return the number of items currently visible in the menu list
int GetCurrentlyVisibleItemsCount();
MenuItem *GetMenuItem(int itemID);
void CloseOtherMenus(MenuItem *item);
virtual void OnKillFocus();
int GetMenuMode();
enum MenuMode
{
MOUSE = 0,
KEYBOARD,
};
void SetCurrentlyHighlightedItem(int itemID);
int GetCurrentlyHighlightedItem();
void ClearCurrentlyHighlightedItem();
// Set the checked state of a checkable menuItem
void SetMenuItemChecked(int itemID, bool state);
bool IsChecked(int index); // check if item is checked.
void SetMinimumWidth(int width);
int GetMinimumWidth();
// baseclass overrides to chain colors through to cascade menus
virtual void SetFgColor( Color newColor );
virtual void SetBgColor( Color newColor );
virtual void SetFont( HFont font );
// Pass in NULL hotkey to remove hotkey
void SetCurrentKeyBinding( int itemID, char const *hotkey );
void ForceCalculateWidth();
void SetUseFallbackFont( bool bState, HFont hFallback );
protected:
// helper functions
int AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData);
int AddMenuItemKeyValuesCommand(MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData);
// vgui result reporting
virtual void OnCommand( const char *command );
MESSAGE_FUNC_PTR( OnMenuItemSelected, "MenuItemSelected", panel );
virtual void AddScrollBar();
virtual void RemoveScrollBar();
MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" );
virtual void Paint();
virtual void LayoutMenuBorder();
virtual void MakeItemsVisibleInScrollRange( int maxVisibleItems, int nNumPixelsAvailable );
virtual void OnMouseWheeled(int delta);
// Alternate OnKeyTyped behaviors
virtual void OnHotKey(wchar_t unichar);
virtual void OnTypeAhead(wchar_t unichar);
int CountVisibleItems();
void ComputeWorkspaceSize( int& workWide, int& workTall );
int ComputeFullMenuHeightWithInsets();
void CalculateWidth();
void LayoutScrollBar();
void PositionCascadingMenu();
void SizeMenuItems();
void OnCursorMoved(int x, int y);
void OnKeyCodePressed(KeyCode code);
void OnMenuClose();
MESSAGE_FUNC( OnKeyModeSet, "KeyModeSet" );
void SetCurrentlySelectedItem(MenuItem *item);
void SetCurrentlySelectedItem(int itemID);
MESSAGE_FUNC_HANDLE( OnCursorEnteredMenuItem, "CursorEnteredMenuItem", menuItem);
MESSAGE_FUNC_HANDLE( OnCursorExitedMenuItem, "CursorExitedMenuItem", menuItem);
void MoveAlongMenuItemList(int direction, int loopCount);
enum
{
DEFAULT_MENU_ITEM_HEIGHT = 22, // height of items in the menu
MENU_UP = -1, // used for moving up/down list of menu items in the menu
MENU_DOWN = 1
};
#ifdef DBGFLAG_VALIDATE
virtual void Validate( CValidator &validator, char *pchName );
#endif // DBGFLAG_VALIDATE
private:
MenuItem *GetParentMenuItem();
int m_iMenuItemHeight;
int m_iFixedWidth;
int m_iMinimumWidth; // a minimum width the menu has to be if it is not fixed width
int m_iNumVisibleLines; // number of items in menu before scroll bar adds on
ScrollBar *m_pScroller;
CUtlLinkedList<MenuItem*, int> m_MenuItems;
CUtlVector<int> m_VisibleSortedItems;
CUtlVector<int> m_SortedItems; // used for visual
CUtlVector<int> m_Separators; // menu item ids after which separators should be shown
CUtlVector<MenuSeparator *> m_SeparatorPanels;
bool _sizedForScrollBar: 1 ; // whether menu has been sized for a scrollbar
bool m_bUseFallbackFont : 1;
bool _recalculateWidth : 1;
bool m_bUseMenuManager : 1;
int _menuWide;
int m_iCurrentlySelectedItemID;
int m_iInputMode;
int m_iCheckImageWidth; // the size of the check box spot on a checkable menu.
int m_iProportionalScrollBarSize;
Label::Alignment m_Alignment;
Color _borderDark;
int m_iActivatedItem;
HFont m_hItemFont;
HFont m_hFallbackItemFont;
// for managing type ahead
#define TYPEAHEAD_BUFSIZE 256
MenuTypeAheadMode m_eTypeAheadMode;
wchar_t m_szTypeAheadBuf[TYPEAHEAD_BUFSIZE];
int m_iNumTypeAheadChars;
double m_fLastTypeAheadTime;
};
//-----------------------------------------------------------------------------
// Helper class to create menu
//-----------------------------------------------------------------------------
class MenuBuilder
{
public:
MenuBuilder( Menu *pMenu, Panel *pActionTarget );
MenuItem* AddMenuItem( const char *pszButtonText, const char *pszCommand, const char *pszCategoryName );
MenuItem* AddMenuItem( const char *pszButtonText, KeyValues *kvUserData, const char *pszCategoryName );
MenuItem* AddCascadingMenuItem( const char *pszButtonText, Menu *pSubMenu, const char *pszCategoryName );
private:
void AddSepratorIfNeeded( const char *pszCategoryName );
Menu *m_pMenu;
Panel *m_pActionTarget;
const char *m_pszLastCategory;
};
} // namespace vgui
#endif // MENU_H