source-engine-2018-hl2_src/vgui2/vgui_controls/KeyBindingHelpDialog.cpp

356 lines
8.0 KiB
C++
Raw Normal View History

2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "vgui_controls/KeyBindingHelpDialog.h"
#include "vgui_controls/ListPanel.h"
#include "vgui/ISurface.h"
#include "vgui/IVGui.h"
#include "vgui/ILocalize.h"
#include "vgui/IInput.h"
#include "vgui/ISystem.h"
#include "KeyValues.h"
#include "vgui/Cursor.h"
#include "tier1/utldict.h"
#include "vgui_controls/KeyBoardEditorDialog.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
using namespace vgui;
// If the user holds the key bound to help down for this long, then the dialog will stay on automatically
#define KB_HELP_CONTINUE_SHOWING_TIME 1.0
static bool BindingLessFunc( KeyValues * const & lhs, KeyValues * const &rhs )
{
KeyValues *p1, *p2;
p1 = const_cast< KeyValues * >( lhs );
p2 = const_cast< KeyValues * >( rhs );
return ( Q_stricmp( p1->GetString( "Action" ), p2->GetString( "Action" ) ) < 0 ) ? true : false;
}
CKeyBindingHelpDialog::CKeyBindingHelpDialog( Panel *parent, Panel *panelToView, KeyBindingContextHandle_t handle, KeyCode code, int modifiers )
: BaseClass( parent, "KeyBindingHelpDialog" ),
m_Handle( handle ),
m_KeyCode( code ),
m_Modifiers( modifiers ),
m_bPermanent( false )
{
Assert( panelToView );
m_hPanel = panelToView;
m_pList = new ListPanel( this, "KeyBindings" );
m_pList->SetIgnoreDoubleClick( true );
m_pList->AddColumnHeader(0, "Action", "#KBEditorBindingName", 175, 0);
m_pList->AddColumnHeader(1, "Binding", "#KBEditorBinding", 175, 0);
m_pList->AddColumnHeader(2, "Description", "#KBEditorDescription", 300, 0);
LoadControlSettings( "resource/KeyBindingHelpDialog.res" );
if ( panelToView && panelToView->GetName() && panelToView->GetName()[0] )
{
SetTitle( panelToView->GetName(), true );
}
else
{
SetTitle( "#KBHelpDialogTitle", true );
}
SetSmallCaption( true );
SetMinimumSize( 400, 400 );
SetMinimizeButtonVisible( false );
SetMaximizeButtonVisible( false );
SetSizeable( true );
SetMoveable( true );
SetMenuButtonVisible( false );
SetVisible( true );
MoveToCenterOfScreen();
PopulateList();
m_flShowTime = system()->GetCurrentTime();
ivgui()->AddTickSignal( GetVPanel(), 0 );
input()->SetAppModalSurface( GetVPanel() );
}
CKeyBindingHelpDialog::~CKeyBindingHelpDialog()
{
if ( input()->GetAppModalSurface() == GetVPanel() )
{
input()->SetAppModalSurface( 0 );
}
}
void CKeyBindingHelpDialog::OnTick()
{
BaseClass::OnTick();
bool keyStillDown = IsHelpKeyStillBeingHeld();
double curtime = system()->GetCurrentTime();
double elapsed = curtime - m_flShowTime;
// After a second of holding the key, releasing the key will close the dialog
if ( elapsed > KB_HELP_CONTINUE_SHOWING_TIME )
{
if ( !keyStillDown )
{
MarkForDeletion();
return;
}
}
// Otherwise, if they tapped the key within a second and now have released...
else if ( !keyStillDown )
{
// Continue showing dialog indefinitely
ivgui()->RemoveTickSignal( GetVPanel() );
m_bPermanent = true;
}
}
// The key originally bound to help was pressed
void CKeyBindingHelpDialog::HelpKeyPressed()
{
// Don't kill while editor is being shown...
if ( m_hKeyBindingsEditor.Get() )
return;
if ( m_bPermanent )
{
MarkForDeletion();
}
}
bool CKeyBindingHelpDialog::IsHelpKeyStillBeingHeld()
{
bool keyDown = input()->IsKeyDown( m_KeyCode );
if ( !keyDown )
return false;
bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
int modifiers = 0;
if ( shift )
{
modifiers |= MODIFIER_SHIFT;
}
if ( ctrl )
{
modifiers |= MODIFIER_CONTROL;
}
if ( alt )
{
modifiers |= MODIFIER_ALT;
}
if ( modifiers != m_Modifiers )
{
return false;
}
return true;
}
void CKeyBindingHelpDialog::OnCommand( char const *cmd )
{
if ( !Q_stricmp( cmd, "OK" ) ||
!Q_stricmp( cmd, "cancel" ) ||
!Q_stricmp( cmd, "Close" ) )
{
MarkForDeletion();
}
else if ( !Q_stricmp( cmd, "edit" ) )
{
// Show the keybindings edit dialog
if ( m_hKeyBindingsEditor.Get() )
{
delete m_hKeyBindingsEditor.Get();
}
// Don't delete panel if H key is released...
m_hKeyBindingsEditor = new CKeyBoardEditorDialog( this, m_hPanel, m_Handle );
m_hKeyBindingsEditor->DoModal();
ivgui()->RemoveTickSignal( GetVPanel() );
m_bPermanent = true;
}
else
{
BaseClass::OnCommand( cmd );
}
}
void CKeyBindingHelpDialog::OnKeyCodeTyped(vgui::KeyCode code)
{
BaseClass::OnKeyCodeTyped( code );
}
void CKeyBindingHelpDialog::GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps )
{
PanelKeyBindingMap *map = panel->GetKBMap();
while ( map )
{
maps.AddToTail( map );
map = map->baseMap;
}
}
void CKeyBindingHelpDialog::AnsiText( char const *token, char *out, size_t buflen )
{
out[ 0 ] = 0;
wchar_t *str = g_pVGuiLocalize->Find( token );
if ( !str )
{
Q_strncpy( out, token, buflen );
}
else
{
g_pVGuiLocalize->ConvertUnicodeToANSI( str, out, buflen );
}
}
struct ListInfo_t
{
PanelKeyBindingMap *m_pMap;
Panel *m_pPanel;
};
void CKeyBindingHelpDialog::PopulateList()
{
m_pList->DeleteAllItems();
int i, j;
CUtlVector< ListInfo_t > maps;
vgui::Panel *pPanel = m_hPanel;
while ( pPanel->IsKeyBindingChainToParentAllowed() )
{
PanelKeyBindingMap *map = pPanel->GetKBMap();
while ( map )
{
int k;
int c = maps.Count();
for ( k = 0; k < c; ++k )
{
if ( maps[k].m_pMap == map )
break;
}
if ( k == c )
{
int iMap = maps.AddToTail( );
maps[iMap].m_pMap = map;
maps[iMap].m_pPanel = pPanel;
}
map = map->baseMap;
}
pPanel = pPanel->GetParent();
if ( !pPanel )
break;
}
CUtlRBTree< KeyValues *, int > sorted( 0, 0, BindingLessFunc );
// add header item
int c = maps.Count();
for ( i = 0; i < c; ++i )
{
PanelKeyBindingMap *m = maps[ i ].m_pMap;
pPanel = maps[i].m_pPanel;
Assert( m );
int bindings = m->boundkeys.Count();
for ( j = 0; j < bindings; ++j )
{
BoundKey_t *kbMap = &m->boundkeys[ j ];
Assert( kbMap );
// Create a new: blank item
KeyValues *item = new KeyValues( "Item" );
// Fill in data
char loc[ 128 ];
Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
char ansi[ 256 ];
AnsiText( loc, ansi, sizeof( ansi ) );
item->SetString( "Action", ansi );
item->SetWString( "Binding", Panel::KeyCodeModifiersToDisplayString( (KeyCode)kbMap->keycode, kbMap->modifiers ) );
// Find the binding
KeyBindingMap_t *bindingMap = pPanel->LookupBinding( kbMap->bindingname );
if ( bindingMap &&
bindingMap->helpstring )
{
AnsiText( bindingMap->helpstring, ansi, sizeof( ansi ) );
item->SetString( "Description", ansi );
}
item->SetPtr( "Item", kbMap );
sorted.Insert( item );
}
// Now try and find any "unbound" keys...
int mappings = m->entries.Count();
for ( j = 0; j < mappings; ++j )
{
KeyBindingMap_t *kbMap = &m->entries[ j ];
// See if it's bound
CUtlVector< BoundKey_t * > list;
pPanel->LookupBoundKeys( kbMap->bindingname, list );
if ( list.Count() > 0 )
continue;
// Not bound, add a placeholder entry
// Create a new: blank item
KeyValues *item = new KeyValues( "Item" );
// fill in data
char loc[ 128 ];
Q_snprintf( loc, sizeof( loc ), "#%s", kbMap->bindingname );
char ansi[ 256 ];
AnsiText( loc, ansi, sizeof( ansi ) );
item->SetString( "Action", ansi );
item->SetWString( "Binding", L"" );
if ( kbMap->helpstring )
{
AnsiText( kbMap->helpstring, ansi, sizeof( ansi ) );
item->SetString( "Description", ansi );
}
item->SetPtr( "Unbound", kbMap );
sorted.Insert( item );
}
}
for ( j = sorted.FirstInorder() ; j != sorted.InvalidIndex(); j = sorted.NextInorder( j ) )
{
KeyValues *item = sorted[ j ];
// Add to list
m_pList->AddItem( item, 0, false, false );
item->deleteThis();
}
sorted.RemoveAll();
}