hl2_src-leak-2017/src/vgui2/src/VPanel.cpp

783 lines
18 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <vgui/IPanel.h>
#include <vgui/IClientPanel.h>
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui/Cursor.h>
#include "vgui_internal.h"
#include "VPanel.h"
#include "tier0/minidump.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
// Lame copy from Panel
enum PinCorner_e
{
PIN_TOPLEFT = 0,
PIN_TOPRIGHT,
PIN_BOTTOMLEFT,
PIN_BOTTOMRIGHT,
// For sibling pinning
PIN_CENTER_TOP,
PIN_CENTER_RIGHT,
PIN_CENTER_BOTTOM,
PIN_CENTER_LEFT,
NUM_PIN_POINTS,
};
float PinDeltas[NUM_PIN_POINTS][2] =
{
{ 0, 0 }, // PIN_TOPLEFT,
{ 1, 0 }, // PIN_TOPRIGHT,
{ 0, 1 }, // PIN_BOTTOMLEFT,
{ 1, 1 }, // PIN_BOTTOMRIGHT,
{ 0.5, 0 }, // PIN_CENTER_TOP,
{ 1, 0.5 }, // PIN_CENTER_RIGHT,
{ 0.5, 1 }, // PIN_CENTER_BOTTOM,
{ 0, 0.5 }, // PIN_CENTER_LEFT,
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
VPanel::VPanel()
{
_pos[0] = _pos[1] = 0;
_absPos[0] = _absPos[1] = 0;
_size[0] = _size[1] = 0;
_minimumSize[0] = 0;
_minimumSize[1] = 0;
_zpos = 0;
_inset[0] = _inset[1] = _inset[2] = _inset[3] = 0;
_clipRect[0] = _clipRect[1] = _clipRect[2] = _clipRect[3] = 0;
_visible = true;
_enabled = true;
_clientPanel = NULL;
_parent = NULL;
_plat = NULL;
_popup = false;
_isTopmostPopup = false;
_hPanel = INVALID_PANEL;
_mouseInput = true; // by default you want mouse and kb input to this panel
_kbInput = true;
_pinsibling = NULL;
_pinsibling_my_corner = PIN_TOPLEFT;
_pinsibling_their_corner = PIN_TOPLEFT;
m_nThinkTraverseLevel = 0;
_clientPanelHandle = vgui::INVALID_PANEL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
VPanel::~VPanel()
{
// Someone just deleted their parent Panel while it was being used in InternalSolveTraverse().
// This will cause a difficult to debug crash, so we spew out the panel name here in hopes
// it will help track down the offender.
if ( m_nThinkTraverseLevel != 0 )
{
Warning( "Deleting in-use vpanel: %s/%s %p.\n", GetName(), GetClassName(), this );
#ifdef STAGING_ONLY
DebuggerBreak();
#endif
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::TraverseLevel( int val )
{
// Bump up our traverse level.
m_nThinkTraverseLevel += m_nThinkTraverseLevel;
// Bump up our client panel traverse level.
if ( Client() )
{
VPANEL vp = g_pVGui->HandleToPanel( _clientPanelHandle );
if ( vp == vgui::INVALID_PANEL )
{
// This is really bad - we have a Client() pointer that is invalid.
Warning( "Panel '%s/%s' has invalid client: %p.\n", GetName(), GetClassName(), Client() );
#ifdef STAGING_ONLY
DebuggerBreak();
#endif
}
if ( Client()->GetVPanel() )
{
VPanel *vpanel = (VPanel *)Client()->GetVPanel();
vpanel->m_nThinkTraverseLevel += vpanel->m_nThinkTraverseLevel;
}
}
// This doesn't work. It appears we add all kinds of children to various panels in the
// InternalThinkTraverse functions, and that means the refcount is 0 when added, and
// then drops to -1 when we decrement the traverse level.
#if 0
// Bump up our children traverse levels.
CUtlVector< VPanel * > &children = GetChildren();
for ( int i = 0; i < children.Count(); ++i )
{
VPanel *child = children[ i ];
if ( child )
child->m_nThinkTraverseLevel = Max( child->m_nThinkTraverseLevel + val, 0 );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::Init(IClientPanel *attachedClientPanel)
{
_clientPanel = attachedClientPanel;
_clientPanelHandle = g_pVGui->PanelToHandle( attachedClientPanel ? attachedClientPanel->GetVPanel() : 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::Solve()
{
short basePos[2];
basePos[0] = _pos[0];
basePos[1] = _pos[1];
int baseSize[2];
GetSize( baseSize[0], baseSize[1] );
VPanel *parent = GetParent();
if (IsPopup())
{
// if we're a popup, draw at the highest level
parent = (VPanel *)g_pSurface->GetEmbeddedPanel();
}
int pabs[2];
if ( parent )
{
parent->GetAbsPos(pabs[0], pabs[1]);
}
if ( _pinsibling )
{
_pinsibling->Solve();
int sibPos[2];
int sibSize[2];
_pinsibling->GetInternalAbsPos( sibPos[0], sibPos[1] );
_pinsibling->GetSize( sibSize[0], sibSize[1] );
for ( int i = 0; i < 2; i++ )
{
if ( parent )
{
sibPos[i] -= pabs[i];
}
// Determine which direction positive values move in. For center pins, we use screen relative signs.
int iSign = 1;
if ( i == 0 && (_pinsibling_their_corner == PIN_CENTER_LEFT || _pinsibling_their_corner == PIN_TOPLEFT || _pinsibling_their_corner == PIN_BOTTOMLEFT) )
{
iSign = -1;
}
else if ( i == 1 && (_pinsibling_their_corner == PIN_CENTER_TOP || _pinsibling_their_corner == PIN_TOPLEFT || _pinsibling_their_corner == PIN_TOPRIGHT) )
{
iSign = -1;
}
int iPos = sibPos[i] + (sibSize[i] * PinDeltas[_pinsibling_their_corner][i]);
iPos -= (baseSize[i] * PinDeltas[_pinsibling_my_corner][i]);
iPos += basePos[i] * iSign;
basePos[i] = iPos;
}
}
int absX = basePos[0];
int absY = basePos[1];
_absPos[0] = basePos[0];
_absPos[1] = basePos[1];
// put into parent space
int pinset[4] = {0, 0, 0, 0};
if ( parent )
{
parent->GetInset( pinset[0], pinset[1], pinset[2], pinset[3] );
absX += pabs[0] + pinset[0];
absY += pabs[1] + pinset[1];
_absPos[0] = clamp( absX, -32767, 32767 );
_absPos[1] = clamp( absY, -32767, 32767 );
}
// set initial bounds
_clipRect[0] = _absPos[0];
_clipRect[1] = _absPos[1];
int absX2 = absX + baseSize[0];
int absY2 = absY + baseSize[1];
_clipRect[2] = clamp( absX2, -32767, 32767 );
_clipRect[3] = clamp( absY2, -32767, 32767 );
// clip to parent, if we're not a popup
if ( parent && !IsPopup() )
{
int pclip[4];
parent->GetClipRect(pclip[0], pclip[1], pclip[2], pclip[3]);
if (_clipRect[0] < pclip[0])
{
_clipRect[0] = pclip[0];
}
if (_clipRect[1] < pclip[1])
{
_clipRect[1] = pclip[1];
}
if(_clipRect[2] > pclip[2])
{
_clipRect[2] = pclip[2] - pinset[2];
}
if(_clipRect[3] > pclip[3])
{
_clipRect[3] = pclip[3] - pinset[3];
}
if ( _clipRect[0] > _clipRect[2] )
{
_clipRect[2] = _clipRect[0];
}
if ( _clipRect[1] > _clipRect[3] )
{
_clipRect[3] = _clipRect[1];
}
}
Assert( _clipRect[0] <= _clipRect[2] );
Assert( _clipRect[1] <= _clipRect[3] );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetPos(int x, int y)
{
_pos[0] = x;
_pos[1] = y;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetPos(int &x, int &y)
{
x = _pos[0];
y = _pos[1];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetSize(int wide,int tall)
{
if (wide<_minimumSize[0])
{
wide=_minimumSize[0];
}
if (tall<_minimumSize[1])
{
tall=_minimumSize[1];
}
if (_size[0] == wide && _size[1] == tall)
return;
_size[0]=wide;
_size[1]=tall;
Client()->OnSizeChanged(wide, tall);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetSize(int& wide,int& tall)
{
wide=_size[0];
tall=_size[1];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetMinimumSize(int wide,int tall)
{
_minimumSize[0]=wide;
_minimumSize[1]=tall;
// check if we're currently smaller than the new minimum size
int currentWidth = _size[0];
if (currentWidth < wide)
{
currentWidth = wide;
}
int currentHeight = _size[1];
if (currentHeight < tall)
{
currentHeight = tall;
}
// resize to new minimum size if necessary
if (currentWidth != _size[0] || currentHeight != _size[1])
{
SetSize(currentWidth, currentHeight);
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetMinimumSize(int &wide, int &tall)
{
wide = _minimumSize[0];
tall = _minimumSize[1];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetVisible(bool state)
{
if (_visible == state)
return;
// need to tell the surface (in case special window processing needs to occur)
g_pSurface->SetPanelVisible((VPANEL)this, state);
_visible = state;
if( IsPopup() )
{
vgui::g_pSurface->CalculateMouseVisible();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetEnabled(bool state)
{
_enabled = state;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool VPanel::IsVisible()
{
return _visible;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool VPanel::IsEnabled()
{
return _enabled;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetAbsPos(int &x, int &y)
{
x = _absPos[0];
y = _absPos[1];
g_pSurface->OffsetAbsPos( x, y );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetInternalAbsPos(int &x, int &y)
{
x = _absPos[0];
y = _absPos[1];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetClipRect(int &x0, int &y0, int &x1, int &y1)
{
x0 = _clipRect[0];
y0 = _clipRect[1];
x1 = _clipRect[2];
y1 = _clipRect[3];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetInset(int left, int top, int right, int bottom)
{
_inset[0] = left;
_inset[1] = top;
_inset[2] = right;
_inset[3] = bottom;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::GetInset(int &left, int &top, int &right, int &bottom)
{
left = _inset[0];
top = _inset[1];
right = _inset[2];
bottom = _inset[3];
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VPanel::SetParent(VPanel *newParent)
{
if (this == newParent)
return;
if (_parent == newParent)
return;
if (_parent != NULL)
{
_parent->_childDar.RemoveElement(this);
_parent = null;
}
if (newParent != NULL)
{
_parent = newParent;
_parent->_childDar.PutElement(this);
SetZPos(_zpos); // re-sort parent's panel order if necessary
if (_parent->Client())
{
_parent->Client()->OnChildAdded((VPANEL)this);
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int VPanel::GetChildCount()
{
return _childDar.GetCount();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
VPanel *VPanel::GetChild(int index)
{
return _childDar[index];
}
CUtlVector< VPanel *> &VPanel::GetChildren()
{
return _childDar;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
VPanel *VPanel::GetParent()
{
return _parent;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the Z position of a panel and reorders it appropriately
//-----------------------------------------------------------------------------
void VPanel::SetZPos(int z)
{
_zpos = z;
if (_parent)
{
// find the child in the list
int childCount = _parent->GetChildCount();
int i;
for (i = 0; i < childCount; i++)
{
if (_parent->GetChild(i) == this)
break;
}
if (i == childCount)
return;
while (1)
{
VPanel *prevChild = NULL, *nextChild = NULL;
if ( i > 0 )
{
prevChild = _parent->GetChild( i - 1 );
}
if ( i <(childCount - 1) )
{
nextChild = _parent->GetChild( i + 1 );
}
// check either side of the child to see if it should move
if ( i > 0 && prevChild && ( prevChild->_zpos > _zpos ) )
{
// swap with the lower
_parent->_childDar.SetElementAt(prevChild, i);
_parent->_childDar.SetElementAt(this, i - 1);
i--;
}
else if (i < (childCount - 1) && nextChild && ( nextChild->_zpos < _zpos ) )
{
// swap with the higher
_parent->_childDar.SetElementAt(nextChild, i);
_parent->_childDar.SetElementAt(this, i + 1);
i++;
}
else
{
break;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: returns the z position of this panel
//-----------------------------------------------------------------------------
int VPanel::GetZPos()
{
return _zpos;
}
//-----------------------------------------------------------------------------
// Purpose: Moves the panel to the front of the z-order
//-----------------------------------------------------------------------------
void VPanel::MoveToFront(void)
{
g_pSurface->MovePopupToFront((VPANEL)this);
if (_parent)
{
// move this panel to the end of it's parents list
_parent->_childDar.MoveElementToEnd(this);
// Validate the Z order
int i = _parent->_childDar.GetCount() - 2;
while (i >= 0)
{
if (_parent->_childDar[i]->_zpos > _zpos)
{
// we can't be in front of this; swap positions
_parent->_childDar.SetElementAt(_parent->_childDar[i], i + 1);
_parent->_childDar.SetElementAt(this, i);
// check the next value
i--;
}
else
{
// order valid
break;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Moves the panel to the back of the z-order
//-----------------------------------------------------------------------------
void VPanel::MoveToBack()
{
if (_parent)
{
// move this panel to the end of it's parents list
_parent->_childDar.RemoveElement(this);
_parent->_childDar.InsertElementAt(this, 0);
// Validate the Z order
int i = 1;
while (i < _parent->_childDar.GetCount())
{
if (_parent->_childDar[i]->_zpos < _zpos)
{
// we can't be behind this; swap positions
_parent->_childDar.SetElementAt(_parent->_childDar[i], i - 1);
_parent->_childDar.SetElementAt(this, i);
// check the next value
i++;
}
else
{
// order valid
break;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Iterates up the hierarchy looking to see if a panel has the specified ancestor
//-----------------------------------------------------------------------------
bool VPanel::HasParent(VPanel *potentialParent)
{
if (this == potentialParent)
return true;
if (_parent)
{
return _parent->HasParent(potentialParent);
}
return false;
}
SurfacePlat *VPanel::Plat()
{
return _plat;
}
void VPanel::SetPlat(SurfacePlat *Plat)
{
_plat = Plat;
}
bool VPanel::IsPopup()
{
return _popup;
}
void VPanel::SetPopup(bool state)
{
_popup = state;
}
bool VPanel::IsTopmostPopup() const
{
return _isTopmostPopup;
}
void VPanel::SetTopmostPopup( bool bEnable )
{
_isTopmostPopup = bEnable;
}
bool VPanel::IsFullyVisible()
{
// recursively check to see if the panel and all it's parents are visible
VPanel *panel = this;
while (panel)
{
if (!panel->_visible)
{
return false;
}
panel = panel->_parent;
}
// we're visible all the way up the hierarchy
return true;
}
const char *VPanel::GetName()
{
return Client()->GetName();
}
const char *VPanel::GetClassName()
{
return Client()->GetClassName();
}
HScheme VPanel::GetScheme()
{
return Client()->GetScheme();
}
void VPanel::SendMessage(KeyValues *params, VPANEL ifrompanel)
{
Client()->OnMessage(params, ifrompanel);
}
void VPanel::SetKeyBoardInputEnabled(bool state)
{
_kbInput = state;
}
void VPanel::SetMouseInputEnabled(bool state)
{
_mouseInput = state;
}
bool VPanel::IsKeyBoardInputEnabled()
{
return _kbInput;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool VPanel::IsMouseInputEnabled()
{
return _mouseInput;
}
//-----------------------------------------------------------------------------
// Purpose: sibling pins
//-----------------------------------------------------------------------------
void VPanel::SetSiblingPin(VPanel *newSibling, byte iMyCornerToPin, byte iSiblingCornerToPinTo )
{
_pinsibling = newSibling;
_pinsibling_my_corner = iMyCornerToPin;
_pinsibling_their_corner = iSiblingCornerToPinTo;
}