hl2_src-leak-2017/src/vgui2/vgui_controls/Frame.cpp

2397 lines
64 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include <assert.h>
#include <math.h> // for ceil()
#define PROTECTED_THINGS_DISABLE
#include "tier1/utlstring.h"
#include "vgui/Cursor.h"
#include "vgui/MouseCode.h"
#include "vgui/IBorder.h"
#include "vgui/IInput.h"
#include "vgui/ILocalize.h"
#include "vgui/IPanel.h"
#include "vgui/ISurface.h"
#include "vgui/IScheme.h"
#include "vgui/KeyCode.h"
#include "vgui_controls/AnimationController.h"
#include "vgui_controls/Controls.h"
#include "vgui_controls/Frame.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/Menu.h"
#include "vgui_controls/MenuButton.h"
#include "vgui_controls/TextImage.h"
#include "KeyValues.h"
#include <stdio.h>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
static const int DEFAULT_SNAP_RANGE = 10; // number of pixels distance before the frame will snap to an edge
static const int CAPTION_TITLE_BORDER = 7;
static const int CAPTION_TITLE_BORDER_SMALL = 0;
namespace
{
//-----------------------------------------------------------------------------
// Purpose: Invisible panel to handle dragging/resizing frames
//-----------------------------------------------------------------------------
class GripPanel : public Panel
{
public:
GripPanel(Frame *dragFrame, const char *name, int xdir, int ydir) : Panel(dragFrame, name)
{
_frame = dragFrame;
_dragging = false;
_dragMultX = xdir;
_dragMultY = ydir;
SetPaintEnabled(false);
SetPaintBackgroundEnabled(false);
SetPaintBorderEnabled(false);
m_iSnapRange = DEFAULT_SNAP_RANGE;
if (xdir == 1 && ydir == 1)
{
// bottom-right grip gets an image
SetPaintEnabled(true);
SetPaintBackgroundEnabled(true);
}
SetBlockDragChaining( true );
}
// Purpose- handle window resizing
// Input- dx, dy, the offet of the mouse pointer from where we started dragging
virtual void moved(int dx, int dy)
{
if (!_frame->IsSizeable())
return;
// Start off with x, y at the coords of where we started to drag
int newX = _dragOrgPos[0], newY =_dragOrgPos[1];
// Start off with width and tall equal from window when we started to drag
int newWide = _dragOrgSize[0], newTall = _dragOrgSize[1];
// get window's minimum size
int minWide, minTall;
_frame->GetMinimumSize( minWide, minTall);
// Handle width resizing
newWide += (dx * _dragMultX);
// Handle the position of the corner x position
if (_dragMultX == -1)
{
// only move if we are not at the minimum
// if we are at min we have to force the proper offset (dx)
if (newWide < minWide)
{
dx=_dragOrgSize[0]-minWide;
}
newX += dx; // move window to its new position
}
// Handle height resizing
newTall += (dy * _dragMultY);
// Handle position of corner y position
if (_dragMultY == -1)
{
if (newTall < minTall)
{
dy=_dragOrgSize[1]-minTall;
}
newY += dy;
}
if ( _frame->GetClipToParent() )
{
// If any coordinate is out of range, snap it back
if ( newX < 0 )
newX = 0;
if ( newY < 0 )
newY = 0;
int sx, sy;
surface()->GetScreenSize( sx, sy );
int w, h;
_frame->GetSize( w, h );
if ( newX + w > sx )
{
newX = sx - w;
}
if ( newY + h > sy )
{
newY = sy - h;
}
}
// set new position
_frame->SetPos(newX, newY);
// set the new size
// if window is below min size it will automatically pop to min size
_frame->SetSize(newWide, newTall);
_frame->InvalidateLayout();
_frame->Repaint();
}
void OnCursorMoved(int x, int y)
{
if (!_dragging)
return;
if (!input()->IsMouseDown(MOUSE_LEFT))
{
// for some reason we're marked as dragging when the mouse is released
// trigger a release
OnMouseReleased(MOUSE_LEFT);
return;
}
input()->GetCursorPos(x, y);
moved((x - _dragStart[0]), ( y - _dragStart[1]));
_frame->Repaint();
}
void OnMousePressed(MouseCode code)
{
if (code == MOUSE_LEFT)
{
_dragging=true;
int x,y;
input()->GetCursorPos(x,y);
_dragStart[0]=x;
_dragStart[1]=y;
_frame->GetPos(_dragOrgPos[0],_dragOrgPos[1]);
_frame->GetSize(_dragOrgSize[0],_dragOrgSize[1]);
input()->SetMouseCapture(GetVPanel());
// if a child doesn't have focus, get it for ourselves
VPANEL focus = input()->GetFocus();
if (!focus || !ipanel()->HasParent(focus, _frame->GetVPanel()))
{
_frame->RequestFocus();
}
_frame->Repaint();
}
else
{
GetParent()->OnMousePressed(code);
}
}
void OnMouseDoublePressed(MouseCode code)
{
GetParent()->OnMouseDoublePressed(code);
}
void Paint()
{
// draw the grab handle in the bottom right of the frame
surface()->DrawSetTextFont(_marlettFont);
surface()->DrawSetTextPos(0, 0);
// thin highlight lines
surface()->DrawSetTextColor(GetFgColor());
surface()->DrawUnicodeChar('p');
}
void PaintBackground()
{
// draw the grab handle in the bottom right of the frame
surface()->DrawSetTextFont(_marlettFont);
surface()->DrawSetTextPos(0, 0);
// thick shadow lines
surface()->DrawSetTextColor(GetBgColor());
surface()->DrawUnicodeChar('o');
}
void OnMouseReleased(MouseCode code)
{
_dragging = false;
input()->SetMouseCapture(NULL);
}
void OnMouseCaptureLost()
{
Panel::OnMouseCaptureLost();
_dragging = false;
}
void ApplySchemeSettings(IScheme *pScheme)
{
Panel::ApplySchemeSettings(pScheme);
bool isSmall = ((Frame *)GetParent())->IsSmallCaption();
_marlettFont = pScheme->GetFont( isSmall ? "MarlettSmall" : "Marlett", IsProportional());
SetFgColor(GetSchemeColor("FrameGrip.Color1", pScheme));
SetBgColor(GetSchemeColor("FrameGrip.Color2", pScheme));
const char *snapRange = pScheme->GetResourceString("Frame.AutoSnapRange");
if (snapRange && *snapRange)
{
m_iSnapRange = atoi(snapRange);
}
}
protected:
Frame *_frame;
int _dragMultX;
int _dragMultY;
bool _dragging;
int _dragOrgPos[2];
int _dragOrgSize[2];
int _dragStart[2];
int m_iSnapRange;
HFont _marlettFont;
};
//-----------------------------------------------------------------------------
// Purpose: Handles caption grip input for moving dialogs around
//-----------------------------------------------------------------------------
class CaptionGripPanel : public GripPanel
{
public:
CaptionGripPanel(Frame* frame, const char *name) : GripPanel(frame, name, 0, 0)
{
}
void moved(int dx, int dy)
{
if (!_frame->IsMoveable())
return;
int newX = _dragOrgPos[0] + dx;
int newY = _dragOrgPos[1] + dy;
if (m_iSnapRange)
{
// first check docking to desktop
int wx, wy, ww, wt;
surface()->GetWorkspaceBounds(wx, wy, ww, wt);
getInsideSnapPosition(wx, wy, ww, wt, newX, newY);
// now lets check all windows and see if we snap to those
// root panel
VPANEL root = surface()->GetEmbeddedPanel();
// cycle through panels
// look for panels that are visible and are popups that we can dock to
for (int i = 0; i < ipanel()->GetChildCount(root); ++i)
{
VPANEL child = ipanel()->GetChild(root, i);
tryToDock (child, newX, newY);
}
}
if ( _frame->GetClipToParent() )
{
// If any coordinate is out of range, snap it back
if ( newX < 0 )
newX = 0;
if ( newY < 0 )
newY = 0;
int sx, sy;
surface()->GetScreenSize( sx, sy );
int w, h;
_frame->GetSize( w, h );
if ( newX + w > sx )
{
newX = sx - w;
}
if ( newY + h > sy )
{
newY = sy - h;
}
}
_frame->SetPos(newX, newY);
}
void tryToDock(VPANEL window, int &newX, int & newY)
{
// bail if child is this window
if ( window == _frame->GetVPanel())
return;
int cx, cy, cw, ct;
if ( (ipanel()->IsVisible(window)) && (ipanel()->IsPopup(window)) )
{
// position
ipanel()->GetAbsPos(window, cx, cy);
// dimensions
ipanel()->GetSize(window, cw, ct);
bool snapped = getOutsideSnapPosition (cx, cy, cw, ct, newX, newY);
if (snapped)
{
// if we snapped, we're done with this path
// dont try to snap to kids
return;
}
}
// check all children
for (int i = 0; i < ipanel()->GetChildCount(window); ++i)
{
VPANEL child = ipanel()->GetChild(window, i);
tryToDock(child, newX, newY);
}
}
// Purpose: To calculate the windows new x,y position if it snaps
// Will snap to the INSIDE of a window (eg desktop sides
// Input: boundX boundY, position of candidate window we are seeing if we snap to
// boundWide, boundTall, width and height of window we are seeing if we snap to
// Output: snapToX, snapToY new coords for window, unchanged if we dont snap
// Returns true if we snapped, false if we did not snap.
bool getInsideSnapPosition(int boundX, int boundY, int boundWide, int boundTall,
int &snapToX, int &snapToY)
{
int wide, tall;
_frame->GetSize(wide, tall);
Assert (wide > 0);
Assert (tall > 0);
bool snapped=false;
if (abs(snapToX - boundX) < m_iSnapRange)
{
snapToX = boundX;
snapped=true;
}
else if (abs((snapToX + wide) - (boundX + boundWide)) < m_iSnapRange)
{
snapToX = boundX + boundWide - wide;
snapped=true;
}
if (abs(snapToY - boundY) < m_iSnapRange)
{
snapToY = boundY;
snapped=true;
}
else if (abs((snapToY + tall) - (boundY + boundTall)) < m_iSnapRange)
{
snapToY = boundY + boundTall - tall;
snapped=true;
}
return snapped;
}
// Purpose: To calculate the windows new x,y position if it snaps
// Will snap to the OUTSIDE edges of a window (i.e. will stick peers together
// Input: left, top, position of candidate window we are seeing if we snap to
// boundWide, boundTall, width and height of window we are seeing if we snap to
// Output: snapToX, snapToY new coords for window, unchanged if we dont snap
// Returns true if we snapped, false if we did not snap.
bool getOutsideSnapPosition(int left, int top, int boundWide, int boundTall,
int &snapToX, int &snapToY)
{
Assert (boundWide >= 0);
Assert (boundTall >= 0);
bool snapped=false;
int right=left+boundWide;
int bottom=top+boundTall;
int wide, tall;
_frame->GetSize(wide, tall);
Assert (wide > 0);
Assert (tall > 0);
// we now see if we are going to be able to snap to a window side, and not
// just snap to the "open air"
// want to make it so that if any part of the window can dock to the candidate, it will
// is this window horizontally snappable to the candidate
bool horizSnappable=(
// top of window is in range
((snapToY > top) && (snapToY < bottom))
// bottom of window is in range
|| ((snapToY+tall > top) && (snapToY+tall < bottom))
// window is just plain bigger than the window we wanna dock to
|| ((snapToY < top) && (snapToY+tall > bottom)) );
// is this window vertically snappable to the candidate
bool vertSnappable= (
// left of window is in range
((snapToX > left) && (snapToX < right))
// right of window is in range
|| ((snapToX+wide > left) && (snapToX+wide < right))
// window is just plain bigger than the window we wanna dock to
|| ((snapToX < left) && (snapToX+wide > right)) );
// if neither, might as well bail
if ( !(horizSnappable || vertSnappable) )
return false;
//if we're within the snap threshold then snap
if ( (snapToX <= (right+m_iSnapRange)) &&
(snapToX >= (right-m_iSnapRange)) )
{
if (horizSnappable)
{
//disallow "open air" snaps
snapped=true;
snapToX = right;
}
}
else if ((snapToX + wide) >= (left-m_iSnapRange) &&
(snapToX + wide) <= (left+m_iSnapRange))
{
if (horizSnappable)
{
snapped=true;
snapToX = left-wide;
}
}
if ( (snapToY <= (bottom+m_iSnapRange)) &&
(snapToY >= (bottom-m_iSnapRange)) )
{
if (vertSnappable)
{
snapped=true;
snapToY = bottom;
}
}
else if ((snapToY + tall) <= (top+m_iSnapRange) &&
(snapToY + tall) >= (top-m_iSnapRange))
{
if (vertSnappable)
{
snapped=true;
snapToY = top-tall;
}
}
return snapped;
}
};
}
namespace vgui
{
//-----------------------------------------------------------------------------
// Purpose: overrides normal button drawing to use different colors & borders
//-----------------------------------------------------------------------------
class FrameButton : public Button
{
private:
IBorder *_brightBorder, *_depressedBorder, *_disabledBorder;
Color _enabledFgColor, _enabledBgColor;
Color _disabledFgColor, _disabledBgColor;
bool _disabledLook;
public:
static int GetButtonSide( Frame *pFrame )
{
if ( pFrame->IsSmallCaption() )
{
return 12;
}
return 18;
}
FrameButton(Panel *parent, const char *name, const char *text) : Button(parent, name, text)
{
SetSize( FrameButton::GetButtonSide( (Frame *)parent ), FrameButton::GetButtonSide( (Frame *)parent ) );
_brightBorder = NULL;
_depressedBorder = NULL;
_disabledBorder = NULL;
_disabledLook = true;
SetContentAlignment(Label::a_northwest);
SetTextInset(2, 1);
SetBlockDragChaining( true );
}
virtual void ApplySchemeSettings(IScheme *pScheme)
{
Button::ApplySchemeSettings(pScheme);
_enabledFgColor = GetSchemeColor("FrameTitleButton.FgColor", pScheme);
_enabledBgColor = GetSchemeColor("FrameTitleButton.BgColor", pScheme);
_disabledFgColor = GetSchemeColor("FrameTitleButton.DisabledFgColor", pScheme);
_disabledBgColor = GetSchemeColor("FrameTitleButton.DisabledBgColor", pScheme);
_brightBorder = pScheme->GetBorder("TitleButtonBorder");
_depressedBorder = pScheme->GetBorder("TitleButtonDepressedBorder");
_disabledBorder = pScheme->GetBorder("TitleButtonDisabledBorder");
SetDisabledLook(_disabledLook);
}
virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
{
if (_disabledLook)
{
return _disabledBorder;
}
if (depressed)
{
return _depressedBorder;
}
return _brightBorder;
}
virtual void SetDisabledLook(bool state)
{
_disabledLook = state;
if (!_disabledLook)
{
SetDefaultColor(_enabledFgColor, _enabledBgColor);
SetArmedColor(_enabledFgColor, _enabledBgColor);
SetDepressedColor(_enabledFgColor, _enabledBgColor);
}
else
{
// setup disabled colors
SetDefaultColor(_disabledFgColor, _disabledBgColor);
SetArmedColor(_disabledFgColor, _disabledBgColor);
SetDepressedColor(_disabledFgColor, _disabledBgColor);
}
}
virtual void PerformLayout()
{
Button::PerformLayout();
Repaint();
}
// Don't request focus.
// This will keep items in the listpanel selected.
virtual void OnMousePressed(MouseCode code)
{
if (!IsEnabled())
return;
if (!IsMouseClickEnabled(code))
return;
if (IsUseCaptureMouseEnabled())
{
{
SetSelected(true);
Repaint();
}
// lock mouse input to going to this button
input()->SetMouseCapture(GetVPanel());
}
}
};
//-----------------------------------------------------------------------------
// Purpose: icon button
//-----------------------------------------------------------------------------
class FrameSystemButton : public MenuButton
{
DECLARE_CLASS_SIMPLE( FrameSystemButton, MenuButton );
private:
IImage *_enabled, *_disabled;
Color _enCol, _disCol;
bool _respond;
CUtlString m_EnabledImage;
CUtlString m_DisabledImage;
public:
FrameSystemButton(Panel *parent, const char *panelName) : MenuButton(parent, panelName, "")
{
_disabled = _enabled = NULL;
_respond = true;
SetEnabled(false);
// This menu will open if we use the left or right mouse button
SetMouseClickEnabled( MOUSE_RIGHT, true );
SetBlockDragChaining( true );
}
void SetImages( const char *pEnabledImage, const char *pDisabledImage = NULL )
{
m_EnabledImage = pEnabledImage;
m_DisabledImage = pDisabledImage ? pDisabledImage : pEnabledImage;
}
void GetImageSize( int &w, int &h )
{
w = h = 0;
int tw = 0, th = 0;
if ( _enabled )
{
_enabled->GetSize( w, h );
}
if ( _disabled )
{
_disabled->GetSize( tw, th );
}
if ( tw > w )
{
w = tw;
}
if ( th > h )
{
h = th;
}
}
virtual void ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
_enCol = GetSchemeColor("FrameSystemButton.FgColor", pScheme);
_disCol = GetSchemeColor("FrameSystemButton.BgColor", pScheme);
const char *pEnabledImage = m_EnabledImage.Length() ? m_EnabledImage.Get() :
pScheme->GetResourceString( "FrameSystemButton.Icon" );
const char *pDisabledImage = m_DisabledImage.Length() ? m_DisabledImage.Get() :
pScheme->GetResourceString( "FrameSystemButton.DisabledIcon" );
_enabled = scheme()->GetImage( pEnabledImage, false);
_disabled = scheme()->GetImage( pDisabledImage, false);
SetTextInset(0, 0);
// get our iconic image
SetEnabled(IsEnabled());
}
virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
{
return NULL;
}
virtual void SetEnabled(bool state)
{
Button::SetEnabled(state);
if (IsEnabled())
{
if ( _enabled )
{
SetImageAtIndex(0, _enabled, 0);
}
SetBgColor(_enCol);
SetDefaultColor(_enCol, _enCol);
SetArmedColor(_enCol, _enCol);
SetDepressedColor(_enCol, _enCol);
}
else
{
if ( _disabled )
{
SetImageAtIndex(0, _disabled, 0);
}
SetBgColor(_disCol);
SetDefaultColor(_disCol, _disCol);
SetArmedColor(_disCol, _disCol);
SetDepressedColor(_disCol, _disCol);
}
}
void SetResponsive(bool state)
{
_respond = state;
}
virtual void OnMousePressed(MouseCode code)
{
// button may look enabled but not be responsive
if (!_respond)
return;
BaseClass::OnMousePressed(code);
}
virtual void OnMouseDoublePressed(MouseCode code)
{
// button may look enabled but not be responsive
if (!_respond)
return;
// only close if left is double pressed
if (code == MOUSE_LEFT)
{
// double click on the icon closes the window
// But only if the menu contains a 'close' item
vgui::Menu *pMenu = GetMenu();
if ( pMenu && pMenu->FindChildByName("Close") )
{
PostMessage(GetVParent(), new KeyValues("CloseFrameButtonPressed"));
}
}
}
};
} // namespace vgui
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
Frame::Frame(Panel *parent, const char *panelName, bool showTaskbarIcon /*=true*/, bool bPopup /*=true*/ ) : EditablePanel(parent, panelName)
{
// frames start invisible, to avoid having window flicker in on taskbar
SetVisible(false);
if ( bPopup )
{
MakePopup(showTaskbarIcon);
}
m_hPreviousModal = 0;
_title=null;
_moveable=true;
_sizeable=true;
m_bHasFocus=false;
_flashWindow=false;
_drawTitleBar = true;
m_bPreviouslyVisible = false;
m_bFadingOut = false;
m_bDisableFadeEffect = false;
m_flTransitionEffectTime = 0.0f;
m_flFocusTransitionEffectTime = 0.0f;
m_bDeleteSelfOnClose = false;
m_iClientInsetX = 5;
m_iClientInsetY = 5;
m_iClientInsetXOverridden = false;
m_iTitleTextInsetX = 28;
m_bClipToParent = false;
m_bSmallCaption = false;
m_bChainKeysToParent = false;
m_bPrimed = false;
m_hCustomTitleFont = INVALID_FONT;
SetTitle("#Frame_Untitled", parent ? false : true);
// add ourselves to the build group
SetBuildGroup(GetBuildGroup());
SetMinimumSize(128,66);
GetFocusNavGroup().SetFocusTopLevel(true);
#if !defined( _X360 )
_sysMenu = NULL;
// add dragging grips
_topGrip = new GripPanel(this, "frame_topGrip", 0, -1);
_bottomGrip = new GripPanel(this, "frame_bottomGrip", 0, 1);
_leftGrip = new GripPanel(this, "frame_leftGrip", -1, 0);
_rightGrip = new GripPanel(this, "frame_rightGrip", 1, 0);
_topLeftGrip = new GripPanel(this, "frame_tlGrip", -1, -1);
_topRightGrip = new GripPanel(this, "frame_trGrip", 1, -1);
_bottomLeftGrip = new GripPanel(this, "frame_blGrip", -1, 1);
_bottomRightGrip = new GripPanel(this, "frame_brGrip", 1, 1);
_captionGrip = new CaptionGripPanel(this, "frame_caption" );
_captionGrip->SetCursor(dc_arrow);
_minimizeButton = new FrameButton(this, "frame_minimize","0");
_minimizeButton->AddActionSignalTarget(this);
_minimizeButton->SetCommand(new KeyValues("Minimize"));
_maximizeButton = new FrameButton(this, "frame_maximize", "1");
//!! no maximize handler implemented yet, so leave maximize button disabled
SetMaximizeButtonVisible(false);
char str[] = { 0x6F, 0 };
_minimizeToSysTrayButton = new FrameButton(this, "frame_mintosystray", str);
_minimizeToSysTrayButton->SetCommand("MinimizeToSysTray");
SetMinimizeToSysTrayButtonVisible(false);
_closeButton = new FrameButton(this, "frame_close", "r");
_closeButton->AddActionSignalTarget(this);
_closeButton->SetCommand(new KeyValues("CloseFrameButtonPressed"));
if (!surface()->SupportsFeature(ISurface::FRAME_MINIMIZE_MAXIMIZE))
{
SetMinimizeButtonVisible(false);
SetMaximizeButtonVisible(false);
}
if (parent)
{
// vgui doesn't support subwindow minimization
SetMinimizeButtonVisible(false);
SetMaximizeButtonVisible(false);
}
_menuButton = new FrameSystemButton(this, "frame_menu");
_menuButton->SetMenu(GetSysMenu());
#endif
SetupResizeCursors();
REGISTER_COLOR_AS_OVERRIDABLE( m_InFocusBgColor, "infocus_bgcolor_override" );
REGISTER_COLOR_AS_OVERRIDABLE( m_OutOfFocusBgColor, "outoffocus_bgcolor_override" );
REGISTER_COLOR_AS_OVERRIDABLE( _titleBarBgColor, "titlebarbgcolor_override" );
REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledBgColor, "titlebardisabledbgcolor_override" );
REGISTER_COLOR_AS_OVERRIDABLE( _titleBarFgColor, "titlebarfgcolor_override" );
REGISTER_COLOR_AS_OVERRIDABLE( _titleBarDisabledFgColor, "titlebardisabledfgcolor_override" );
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
Frame::~Frame()
{
if ( input()->GetAppModalSurface() == GetVPanel() )
{
vgui::input()->ReleaseAppModalSurface();
if ( m_hPreviousModal != 0 )
{
vgui::input()->SetAppModalSurface( m_hPreviousModal );
m_hPreviousModal = 0;
}
}
#if !defined( _X360 )
delete _topGrip;
delete _bottomGrip;
delete _leftGrip;
delete _rightGrip;
delete _topLeftGrip;
delete _topRightGrip;
delete _bottomLeftGrip;
delete _bottomRightGrip;
delete _captionGrip;
delete _minimizeButton;
delete _maximizeButton;
delete _closeButton;
delete _menuButton;
delete _minimizeToSysTrayButton;
#endif
delete _title;
}
//-----------------------------------------------------------------------------
// Purpose: Setup the grips on the edges of the panel to resize it.
//-----------------------------------------------------------------------------
void Frame::SetupResizeCursors()
{
#if !defined( _X360 )
if (IsSizeable())
{
_topGrip->SetCursor(dc_sizens);
_bottomGrip->SetCursor(dc_sizens);
_leftGrip->SetCursor(dc_sizewe);
_rightGrip->SetCursor(dc_sizewe);
_topLeftGrip->SetCursor(dc_sizenwse);
_topRightGrip->SetCursor(dc_sizenesw);
_bottomLeftGrip->SetCursor(dc_sizenesw);
_bottomRightGrip->SetCursor(dc_sizenwse);
_bottomRightGrip->SetPaintEnabled(true);
_bottomRightGrip->SetPaintBackgroundEnabled(true);
}
else
{
// not resizable, so just use the default cursor
_topGrip->SetCursor(dc_arrow);
_bottomGrip->SetCursor(dc_arrow);
_leftGrip->SetCursor(dc_arrow);
_rightGrip->SetCursor(dc_arrow);
_topLeftGrip->SetCursor(dc_arrow);
_topRightGrip->SetCursor(dc_arrow);
_bottomLeftGrip->SetCursor(dc_arrow);
_bottomRightGrip->SetCursor(dc_arrow);
_bottomRightGrip->SetPaintEnabled(false);
_bottomRightGrip->SetPaintBackgroundEnabled(false);
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Bring the frame to the front and requests focus, ensures it's not minimized
//-----------------------------------------------------------------------------
void Frame::Activate()
{
MoveToFront();
if ( IsKeyBoardInputEnabled() )
{
RequestFocus();
}
SetVisible(true);
SetEnabled(true);
if (m_bFadingOut)
{
// we were fading out, make sure to fade back in
m_bFadingOut = false;
m_bPreviouslyVisible = false;
}
surface()->SetMinimized(GetVPanel(), false);
}
//-----------------------------------------------------------------------------
// Sets up, cleans up modal dialogs
//-----------------------------------------------------------------------------
void Frame::DoModal( )
{
// move to the middle of the screen
MoveToCenterOfScreen();
InvalidateLayout();
Activate();
m_hPreviousModal = vgui::input()->GetAppModalSurface();
vgui::input()->SetAppModalSurface( GetVPanel() );
}
//-----------------------------------------------------------------------------
// Closes a modal dialog
//-----------------------------------------------------------------------------
void Frame::CloseModal()
{
vgui::input()->ReleaseAppModalSurface();
if ( m_hPreviousModal != 0 )
{
vgui::input()->SetAppModalSurface( m_hPreviousModal );
m_hPreviousModal = 0;
}
PostMessage( this, new KeyValues("Close") );
}
//-----------------------------------------------------------------------------
// Purpose: activates the dialog
// if dialog is not currently visible it starts it minimized and flashing in the taskbar
//-----------------------------------------------------------------------------
void Frame::ActivateMinimized()
{
if ( ( IsVisible() && !IsMinimized() ) || !surface()->SupportsFeature( ISurface::FRAME_MINIMIZE_MAXIMIZE ) )
{
Activate();
}
else
{
ipanel()->MoveToBack(GetVPanel());
surface()->SetMinimized(GetVPanel(), true);
SetVisible(true);
SetEnabled(true);
if (m_bFadingOut)
{
// we were fading out, make sure to fade back in
m_bFadingOut = false;
m_bPreviouslyVisible = false;
}
FlashWindow();
}
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the dialog is currently minimized
//-----------------------------------------------------------------------------
bool Frame::IsMinimized()
{
return surface()->IsMinimized(GetVPanel());
}
//-----------------------------------------------------------------------------
// Purpose: Center the dialog on the screen
//-----------------------------------------------------------------------------
void Frame::MoveToCenterOfScreen()
{
int wx, wy, ww, wt;
surface()->GetWorkspaceBounds(wx, wy, ww, wt);
SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2);
}
void Frame::LayoutProportional( FrameButton *bt )
{
float scale = 1.0;
if( IsProportional() )
{
int screenW, screenH;
surface()->GetScreenSize( screenW, screenH );
int proW,proH;
surface()->GetProportionalBase( proW, proH );
scale = ( (float)( screenH ) / (float)( proH ) );
}
bt->SetSize( (int)( FrameButton::GetButtonSide( this ) * scale ), (int)( FrameButton::GetButtonSide( this ) * scale ) );
bt->SetTextInset( (int)( ceil( 2 * scale ) ), (int) ( ceil(1 * scale ) ) );
}
//-----------------------------------------------------------------------------
// Purpose: per-frame thinking, used for transition effects
// only gets called if the Frame is visible
//-----------------------------------------------------------------------------
void Frame::OnThink()
{
BaseClass::OnThink();
// check for transition effects
if (IsVisible() && m_flTransitionEffectTime > 0 && ( !m_bDisableFadeEffect ))
{
if (m_bFadingOut)
{
// we're fading out, see if we're done so we can fully hide the window
if (GetAlpha() < ( IsX360() ? 64 : 1 ))
{
FinishClose();
}
}
else if (!m_bPreviouslyVisible)
{
// need to fade-in
m_bPreviouslyVisible = true;
// fade in
if (IsX360())
{
SetAlpha(64);
}
else
{
SetAlpha(0);
}
GetAnimationController()->RunAnimationCommand(this, "alpha", 255.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
}
}
// check for focus changes
bool hasFocus = false;
if (input())
{
VPANEL focus = input()->GetFocus();
if (focus && ipanel()->HasParent(focus, GetVPanel()))
{
if ( input()->GetAppModalSurface() == 0 ||
input()->GetAppModalSurface() == GetVPanel() )
{
hasFocus = true;
}
}
}
if (hasFocus != m_bHasFocus)
{
// Because vgui focus is message based, and focus gets reset to NULL when a focused panel is deleted, we defer the flashing/transition
// animation for an extra frame in case something is deleted, a message is sent, and then we become the focused panel again on the
// next frame
if ( !m_bPrimed )
{
m_bPrimed = true;
return;
}
m_bPrimed = false;
m_bHasFocus = hasFocus;
OnFrameFocusChanged(m_bHasFocus);
}
else
{
m_bPrimed = false;
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when the frame focus changes
//-----------------------------------------------------------------------------
void Frame::OnFrameFocusChanged(bool bHasFocus)
{
#if !defined( _X360 )
// enable/disable the frame buttons
_minimizeButton->SetDisabledLook(!bHasFocus);
_maximizeButton->SetDisabledLook(!bHasFocus);
_closeButton->SetDisabledLook(!bHasFocus);
_minimizeToSysTrayButton->SetDisabledLook(!bHasFocus);
_menuButton->SetEnabled(bHasFocus);
_minimizeButton->InvalidateLayout();
_maximizeButton->InvalidateLayout();
_minimizeToSysTrayButton->InvalidateLayout();
_closeButton->InvalidateLayout();
_menuButton->InvalidateLayout();
#endif
if (bHasFocus)
{
_title->SetColor(_titleBarFgColor);
}
else
{
_title->SetColor(_titleBarDisabledFgColor);
}
// set our background color
if (bHasFocus)
{
if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect ))
{
GetAnimationController()->RunAnimationCommand(this, "BgColor", m_InFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
}
else
{
SetBgColor(m_InFocusBgColor);
}
}
else
{
if (m_flFocusTransitionEffectTime && ( !m_bDisableFadeEffect ))
{
GetAnimationController()->RunAnimationCommand(this, "BgColor", m_OutOfFocusBgColor, 0.0f, m_bDisableFadeEffect ? 0.0f : m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
}
else
{
SetBgColor(m_OutOfFocusBgColor);
}
}
// Stop flashing when we get focus
if (bHasFocus && _flashWindow)
{
FlashWindowStop();
}
}
int Frame::GetDraggerSize()
{
const int DRAGGER_SIZE = 5;
if ( m_bSmallCaption )
{
return 3;
}
return DRAGGER_SIZE;
}
int Frame::GetCornerSize()
{
const int CORNER_SIZE = 8;
if ( m_bSmallCaption )
{
return 6;
}
return CORNER_SIZE;
}
int Frame::GetBottomRightSize()
{
const int BOTTOMRIGHTSIZE = 18;
if ( m_bSmallCaption )
{
return 12;
}
return BOTTOMRIGHTSIZE;
}
int Frame::GetCaptionHeight()
{
const int CAPTIONHEIGHT = 23;
if ( m_bSmallCaption )
{
return 12;
}
return CAPTIONHEIGHT;
}
//-----------------------------------------------------------------------------
// Purpose: Recalculate the position of all items
//-----------------------------------------------------------------------------
void Frame::PerformLayout()
{
// chain back
BaseClass::PerformLayout();
// move everything into place
int wide, tall;
GetSize(wide, tall);
#if !defined( _X360 )
int DRAGGER_SIZE = GetDraggerSize();
int CORNER_SIZE = GetCornerSize();
int CORNER_SIZE2 = CORNER_SIZE * 2;
int BOTTOMRIGHTSIZE = GetBottomRightSize();
_topGrip->SetBounds(CORNER_SIZE, 0, wide - CORNER_SIZE2, DRAGGER_SIZE);
_leftGrip->SetBounds(0, CORNER_SIZE, DRAGGER_SIZE, tall - CORNER_SIZE2);
_topLeftGrip->SetBounds(0, 0, CORNER_SIZE, CORNER_SIZE);
_topRightGrip->SetBounds(wide - CORNER_SIZE, 0, CORNER_SIZE, CORNER_SIZE);
_bottomLeftGrip->SetBounds(0, tall - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE);
// make the bottom-right grip larger
_bottomGrip->SetBounds(CORNER_SIZE, tall - DRAGGER_SIZE, wide - (CORNER_SIZE + BOTTOMRIGHTSIZE), DRAGGER_SIZE);
_rightGrip->SetBounds(wide - DRAGGER_SIZE, CORNER_SIZE, DRAGGER_SIZE, tall - (CORNER_SIZE + BOTTOMRIGHTSIZE));
_bottomRightGrip->SetBounds(wide - BOTTOMRIGHTSIZE, tall - BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE, BOTTOMRIGHTSIZE);
_captionGrip->SetSize(wide-10,GetCaptionHeight());
_topGrip->MoveToFront();
_bottomGrip->MoveToFront();
_leftGrip->MoveToFront();
_rightGrip->MoveToFront();
_topLeftGrip->MoveToFront();
_topRightGrip->MoveToFront();
_bottomLeftGrip->MoveToFront();
_bottomRightGrip->MoveToFront();
_maximizeButton->MoveToFront();
_menuButton->MoveToFront();
_minimizeButton->MoveToFront();
_minimizeToSysTrayButton->MoveToFront();
_menuButton->SetBounds(5+2, 5+3, GetCaptionHeight()-5, GetCaptionHeight()-5);
#endif
float scale = 1;
if (IsProportional())
{
int screenW, screenH;
surface()->GetScreenSize( screenW, screenH );
int proW,proH;
surface()->GetProportionalBase( proW, proH );
scale = ( (float)( screenH ) / (float)( proH ) );
}
#if !defined( _X360 )
int offset_start = (int)( 20 * scale );
int offset = offset_start;
int top_border_offset = (int) ( ( 5+3 ) * scale );
if ( m_bSmallCaption )
{
top_border_offset = (int) ( ( 3 ) * scale );
}
int side_border_offset = (int) ( 5 * scale );
// push the buttons against the east side
if (_closeButton->IsVisible())
{
_closeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
offset += offset_start;
LayoutProportional( _closeButton );
}
if (_minimizeToSysTrayButton->IsVisible())
{
_minimizeToSysTrayButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
offset += offset_start;
LayoutProportional( _minimizeToSysTrayButton );
}
if (_maximizeButton->IsVisible())
{
_maximizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
offset += offset_start;
LayoutProportional( _maximizeButton );
}
if (_minimizeButton->IsVisible())
{
_minimizeButton->SetPos((wide-side_border_offset)-offset,top_border_offset);
offset += offset_start;
LayoutProportional( _minimizeButton );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Set the text in the title bar.
//-----------------------------------------------------------------------------
void Frame::SetTitle(const char *title, bool surfaceTitle)
{
if (!_title)
{
_title = new TextImage( "" );
}
Assert(title);
_title->SetText(title);
// see if the combobox text has changed, and if so, post a message detailing the new text
const char *newTitle = title;
// check if the new text is a localized string, if so undo it
wchar_t unicodeText[128];
unicodeText[0] = 0;
if (*newTitle == '#')
{
// try lookup in localization tables
StringIndex_t unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(newTitle + 1);
if (unlocalizedTextSymbol != INVALID_LOCALIZE_STRING_INDEX)
{
// we have a new text value
wcsncpy( unicodeText, g_pVGuiLocalize->GetValueByIndex(unlocalizedTextSymbol), sizeof( unicodeText) / sizeof(wchar_t) );
}
}
else
{
g_pVGuiLocalize->ConvertANSIToUnicode( newTitle, unicodeText, sizeof(unicodeText) );
}
if (surfaceTitle)
{
surface()->SetTitle(GetVPanel(), unicodeText);
}
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the unicode text in the title bar
//-----------------------------------------------------------------------------
void Frame::SetTitle(const wchar_t *title, bool surfaceTitle)
{
if (!_title)
{
_title = new TextImage( "" );
}
_title->SetText(title);
if (surfaceTitle)
{
surface()->SetTitle(GetVPanel(), title);
}
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Set the text in the title bar.
//-----------------------------------------------------------------------------
void Frame::InternalSetTitle(const char *title)
{
SetTitle(title, true);
}
//-----------------------------------------------------------------------------
// Purpose: Set the movability of the panel
//-----------------------------------------------------------------------------
void Frame::SetMoveable(bool state)
{
_moveable=state;
}
//-----------------------------------------------------------------------------
// Purpose: Set the resizability of the panel
//-----------------------------------------------------------------------------
void Frame::SetSizeable(bool state)
{
_sizeable=state;
SetupResizeCursors();
}
// When moving via caption, don't let any part of window go outside parent's bounds
void Frame::SetClipToParent( bool state )
{
m_bClipToParent = state;
}
bool Frame::GetClipToParent() const
{
return m_bClipToParent;
}
//-----------------------------------------------------------------------------
// Purpose: Check the movability of the panel
//-----------------------------------------------------------------------------
bool Frame::IsMoveable()
{
return _moveable;
}
//-----------------------------------------------------------------------------
// Purpose: Check the resizability of the panel
//-----------------------------------------------------------------------------
bool Frame::IsSizeable()
{
return _sizeable;
}
//-----------------------------------------------------------------------------
// Purpose: Get the size of the panel inside the frame edges.
//-----------------------------------------------------------------------------
void Frame::GetClientArea(int &x, int &y, int &wide, int &tall)
{
x = m_iClientInsetX;
GetSize(wide, tall);
if (_drawTitleBar)
{
int captionTall = surface()->GetFontTall(_title->GetFont());
int border = m_bSmallCaption ? CAPTION_TITLE_BORDER_SMALL : CAPTION_TITLE_BORDER;
int yinset = m_bSmallCaption ? 0 : m_iClientInsetY;
yinset += m_iTitleTextInsetYOverride;
y = yinset + captionTall + border + 1;
tall = (tall - yinset) - y;
}
if ( m_bSmallCaption )
{
tall -= 5;
}
wide = (wide - m_iClientInsetX) - x;
}
//
//-----------------------------------------------------------------------------
// Purpose: applies user configuration settings
//-----------------------------------------------------------------------------
void Frame::ApplyUserConfigSettings(KeyValues *userConfig)
{
// calculate defaults
int wx, wy, ww, wt;
vgui::surface()->GetWorkspaceBounds(wx, wy, ww, wt);
int x, y, wide, tall;
GetBounds(x, y, wide, tall);
bool bNoSettings = false;
if (_moveable)
{
// check to see if anything is set
if (!userConfig->FindKey("xpos", false))
{
bNoSettings = true;
}
// get the user config position
// default to where we're currently at
x = userConfig->GetInt("xpos", x);
y = userConfig->GetInt("ypos", y);
}
if (_sizeable)
{
wide = userConfig->GetInt("wide", wide);
tall = userConfig->GetInt("tall", tall);
// Make sure it's no larger than the workspace
if ( wide > ww )
{
wide = ww;
}
if ( tall > wt )
{
tall = wt;
}
}
// see if the dialog has a place on the screen it wants to start
if (bNoSettings && GetDefaultScreenPosition(x, y, wide, tall))
{
bNoSettings = false;
}
// make sure it conforms to the minimum size of the dialog
int minWide, minTall;
GetMinimumSize(minWide, minTall);
if (wide < minWide)
{
wide = minWide;
}
if (tall < minTall)
{
tall = minTall;
}
// make sure it's on the screen
if (x + wide > ww)
{
x = wx + ww - wide;
}
if (y + tall > wt)
{
y = wy + wt - tall;
}
if (x < wx)
{
x = wx;
}
if (y < wy)
{
y = wy;
}
SetBounds(x, y, wide, tall);
if (bNoSettings)
{
// since nothing was set, default our position to the middle of the screen
MoveToCenterOfScreen();
}
BaseClass::ApplyUserConfigSettings(userConfig);
}
//-----------------------------------------------------------------------------
// Purpose: returns user config settings for this control
//-----------------------------------------------------------------------------
void Frame::GetUserConfigSettings(KeyValues *userConfig)
{
if (_moveable)
{
int x, y;
GetPos(x, y);
userConfig->SetInt("xpos", x);
userConfig->SetInt("ypos", y);
}
if (_sizeable)
{
int w, t;
GetSize(w, t);
userConfig->SetInt("wide", w);
userConfig->SetInt("tall", t);
}
BaseClass::GetUserConfigSettings(userConfig);
}
//-----------------------------------------------------------------------------
// Purpose: optimization, return true if this control has any user config settings
//-----------------------------------------------------------------------------
bool Frame::HasUserConfigSettings()
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: gets the default position and size on the screen to appear the first time (defaults to centered)
//-----------------------------------------------------------------------------
bool Frame::GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall)
{
return false;
}
//-----------------------------------------------------------------------------
// Purpose: draws title bar
//-----------------------------------------------------------------------------
void Frame::PaintBackground()
{
// take the panel with focus and check up tree for this panel
// if you find it, than some child of you has the focus, so
// you should be focused
Color titleColor = _titleBarDisabledBgColor;
if (m_bHasFocus)
{
titleColor = _titleBarBgColor;
}
BaseClass::PaintBackground();
if (_drawTitleBar)
{
int wide = GetWide();
int tall = surface()->GetFontTall(_title->GetFont());
// caption
surface()->DrawSetColor(titleColor);
int inset = m_bSmallCaption ? 3 : 5;
int captionHeight = m_bSmallCaption ? 14: 28;
surface()->DrawFilledRect(inset, inset, wide - inset, captionHeight );
if (_title)
{
int nTitleX = m_iTitleTextInsetXOverride ? m_iTitleTextInsetXOverride : m_iTitleTextInsetX;
int nTitleWidth = wide - 72;
#if !defined( _X360 )
if ( _menuButton && _menuButton->IsVisible() )
{
int mw, mh;
_menuButton->GetImageSize( mw, mh );
nTitleX += mw;
nTitleWidth -= mw;
}
#endif
int nTitleY;
if ( m_iTitleTextInsetYOverride )
{
nTitleY = m_iTitleTextInsetYOverride;
}
else
{
nTitleY = m_bSmallCaption ? 2 : 9;
}
_title->SetPos( nTitleX, nTitleY );
_title->SetSize( nTitleWidth, tall);
_title->Paint();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Frame::ApplySchemeSettings(IScheme *pScheme)
{
// always chain back
BaseClass::ApplySchemeSettings(pScheme);
SetOverridableColor( &_titleBarFgColor, GetSchemeColor("FrameTitleBar.TextColor", pScheme) );
SetOverridableColor( &_titleBarBgColor, GetSchemeColor("FrameTitleBar.BgColor", pScheme) );
SetOverridableColor( &_titleBarDisabledFgColor, GetSchemeColor("FrameTitleBar.DisabledTextColor", pScheme) );
SetOverridableColor( &_titleBarDisabledBgColor, GetSchemeColor("FrameTitleBar.DisabledBgColor", pScheme) );
const char *font = NULL;
if ( m_bSmallCaption )
{
font = pScheme->GetResourceString("FrameTitleBar.SmallFont");
}
else
{
font = pScheme->GetResourceString("FrameTitleBar.Font");
}
HFont titlefont;
if ( m_hCustomTitleFont )
{
titlefont = m_hCustomTitleFont;
}
else
{
titlefont = pScheme->GetFont((font && *font) ? font : "Default", IsProportional());
}
_title->SetFont( titlefont );
_title->ResizeImageToContent();
#if !defined( _X360 )
HFont marfont = (HFont)0;
if ( m_bSmallCaption )
{
marfont = pScheme->GetFont( "MarlettSmall", IsProportional() );
}
else
{
marfont = pScheme->GetFont( "Marlett", IsProportional() );
}
_minimizeButton->SetFont(marfont);
_maximizeButton->SetFont(marfont);
_minimizeToSysTrayButton->SetFont(marfont);
_closeButton->SetFont(marfont);
#endif
m_flTransitionEffectTime = atof(pScheme->GetResourceString("Frame.TransitionEffectTime"));
m_flFocusTransitionEffectTime = atof(pScheme->GetResourceString("Frame.FocusTransitionEffectTime"));
SetOverridableColor( &m_InFocusBgColor, pScheme->GetColor("Frame.BgColor", GetBgColor()) );
SetOverridableColor( &m_OutOfFocusBgColor, pScheme->GetColor("Frame.OutOfFocusBgColor", m_InFocusBgColor) );
const char *resourceString = pScheme->GetResourceString("Frame.ClientInsetX");
if ( resourceString )
{
m_iClientInsetX = atoi(resourceString);
}
resourceString = pScheme->GetResourceString("Frame.ClientInsetY");
if ( resourceString )
{
m_iClientInsetY = atoi(resourceString);
}
resourceString = pScheme->GetResourceString("Frame.TitleTextInsetX");
if ( resourceString )
{
m_iTitleTextInsetX = atoi(resourceString);
}
SetBgColor(m_InFocusBgColor);
SetBorder(pScheme->GetBorder("FrameBorder"));
OnFrameFocusChanged( m_bHasFocus );
}
// Disables the fade-in/out-effect even if configured in the scheme settings
void Frame::DisableFadeEffect( void )
{
m_flFocusTransitionEffectTime = 0.f;
m_flTransitionEffectTime = 0.f;
}
void Frame::SetFadeEffectDisableOverride( bool disabled )
{
m_bDisableFadeEffect = disabled;
}
//-----------------------------------------------------------------------------
// Purpose: Apply settings loaded from a resource file
//-----------------------------------------------------------------------------
void Frame::ApplySettings(KeyValues *inResourceData)
{
// Don't change the frame's visibility, remove that setting from the config data
inResourceData->SetInt("visible", -1);
BaseClass::ApplySettings(inResourceData);
SetCloseButtonVisible( inResourceData->GetBool( "setclosebuttonvisible", true ) );
if( !inResourceData->GetInt("settitlebarvisible", 1 ) ) // if "title" is "0" then don't draw the title bar
{
SetTitleBarVisible( false );
}
// set the title
const char *title = inResourceData->GetString("title", "");
if (title && *title)
{
SetTitle(title, true);
}
const char *titlefont = inResourceData->GetString("title_font", "");
if ( titlefont && titlefont[0] )
{
IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
if ( pScheme )
{
m_hCustomTitleFont = pScheme->GetFont( titlefont );
}
}
KeyValues *pKV = inResourceData->FindKey( "clientinsetx_override", false );
if ( pKV )
{
m_iClientInsetX = pKV->GetInt();
m_iClientInsetXOverridden = true;
}
}
//-----------------------------------------------------------------------------
// Purpose: Apply settings loaded from a resource file
//-----------------------------------------------------------------------------
void Frame::GetSettings(KeyValues *outResourceData)
{
BaseClass::GetSettings(outResourceData);
outResourceData->SetInt("settitlebarvisible", _drawTitleBar );
if (_title)
{
char buf[256];
_title->GetUnlocalizedText( buf, 255 );
if (buf[0])
{
outResourceData->SetString("title", buf);
}
}
if ( m_iClientInsetXOverridden )
{
outResourceData->SetInt( "clientinsetx_override", m_iClientInsetX );
}
}
//-----------------------------------------------------------------------------
// Purpose: returns a description of the settings possible for a frame
//-----------------------------------------------------------------------------
const char *Frame::GetDescription()
{
static char buf[512];
Q_snprintf(buf, sizeof(buf), "%s, string title", BaseClass::GetDescription());
return buf;
}
//-----------------------------------------------------------------------------
// Purpose: Go invisible when a close message is recieved.
//-----------------------------------------------------------------------------
void Frame::OnClose()
{
// if we're modal, release that before we hide the window else the wrong window will get focus
if (input()->GetAppModalSurface() == GetVPanel())
{
input()->ReleaseAppModalSurface();
if ( m_hPreviousModal != 0 )
{
vgui::input()->SetAppModalSurface( m_hPreviousModal );
m_hPreviousModal = 0;
}
}
BaseClass::OnClose();
if (m_flTransitionEffectTime && !m_bDisableFadeEffect)
{
// begin the hide transition effect
GetAnimationController()->RunAnimationCommand(this, "alpha", 0.0f, 0.0f, m_flTransitionEffectTime, AnimationController::INTERPOLATOR_LINEAR);
m_bFadingOut = true;
// move us to the back of the draw order (so that fading out over the top of other dialogs doesn't look wierd)
surface()->MovePopupToBack(GetVPanel());
}
else
{
// hide us immediately
FinishClose();
}
}
//-----------------------------------------------------------------------------
// Purpose: Close button in frame pressed
//-----------------------------------------------------------------------------
void Frame::OnCloseFrameButtonPressed()
{
OnCommand("Close");
}
//-----------------------------------------------------------------------------
// Purpose: Command handling
//-----------------------------------------------------------------------------
void Frame::OnCommand(const char *command)
{
if (!stricmp(command, "Close"))
{
Close();
}
else if (!stricmp(command, "CloseModal"))
{
CloseModal();
}
else if (!stricmp(command, "Minimize"))
{
OnMinimize();
}
else if (!stricmp(command, "MinimizeToSysTray"))
{
OnMinimizeToSysTray();
}
else
{
BaseClass::OnCommand(command);
}
}
//-----------------------------------------------------------------------------
// Purpose: Get the system menu
//-----------------------------------------------------------------------------
Menu *Frame::GetSysMenu()
{
#if !defined( _X360 )
if (!_sysMenu)
{
_sysMenu = new Menu(this, NULL);
_sysMenu->SetVisible(false);
_sysMenu->AddActionSignalTarget(this);
_sysMenu->AddMenuItem("Minimize", "#SysMenu_Minimize", "Minimize", this);
_sysMenu->AddMenuItem("Maximize", "#SysMenu_Maximize", "Maximize", this);
_sysMenu->AddMenuItem("Close", "#SysMenu_Close", "Close", this);
// check for enabling/disabling menu items
// this might have to be done at other times as well.
Panel *menuItem = _sysMenu->FindChildByName("Minimize");
if (menuItem)
{
menuItem->SetEnabled(_minimizeButton->IsVisible());
}
menuItem = _sysMenu->FindChildByName("Maximize");
if (menuItem)
{
menuItem->SetEnabled(_maximizeButton->IsVisible());
}
menuItem = _sysMenu->FindChildByName("Close");
if (menuItem)
{
menuItem->SetEnabled(_closeButton->IsVisible());
}
}
return _sysMenu;
#else
return NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Set the system menu
//-----------------------------------------------------------------------------
void Frame::SetSysMenu(Menu *menu)
{
#if !defined( _X360 )
if (menu == _sysMenu)
return;
_sysMenu->MarkForDeletion();
_sysMenu = menu;
_menuButton->SetMenu(_sysMenu);
#endif
}
//-----------------------------------------------------------------------------
// Set the system menu images
//-----------------------------------------------------------------------------
void Frame::SetImages( const char *pEnabledImage, const char *pDisabledImage )
{
#if !defined( _X360 )
_menuButton->SetImages( pEnabledImage, pDisabledImage );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Close the window
//-----------------------------------------------------------------------------
void Frame::Close()
{
OnClose();
}
//-----------------------------------------------------------------------------
// Purpose: Finishes closing the dialog
//-----------------------------------------------------------------------------
void Frame::FinishClose()
{
SetVisible(false);
m_bPreviouslyVisible = false;
m_bFadingOut = false;
OnFinishedClose();
if (m_bDeleteSelfOnClose)
{
// Must be last because if vgui is not running then this will call delete this!!!
MarkForDeletion();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Frame::OnFinishedClose()
{
}
//-----------------------------------------------------------------------------
// Purpose: Minimize the window on the taskbar.
//-----------------------------------------------------------------------------
void Frame::OnMinimize()
{
surface()->SetMinimized(GetVPanel(), true);
}
//-----------------------------------------------------------------------------
// Purpose: Does nothing by default
//-----------------------------------------------------------------------------
void Frame::OnMinimizeToSysTray()
{
}
//-----------------------------------------------------------------------------
// Purpose: Respond to mouse presses
//-----------------------------------------------------------------------------
void Frame::OnMousePressed(MouseCode code)
{
if (!IsBuildGroupEnabled())
{
// if a child doesn't have focus, get it for ourselves
VPANEL focus = input()->GetFocus();
if (!focus || !ipanel()->HasParent(focus, GetVPanel()))
{
RequestFocus();
}
}
BaseClass::OnMousePressed(code);
}
//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the system menu button
//-----------------------------------------------------------------------------
void Frame::SetMenuButtonVisible(bool state)
{
#if !defined( _X360 )
_menuButton->SetVisible(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Toggle respond of the system menu button
// it will look enabled or disabled in response to the title bar
// but may not activate.
//-----------------------------------------------------------------------------
void Frame::SetMenuButtonResponsive(bool state)
{
#if !defined( _X360 )
_menuButton->SetResponsive(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the minimize button
//-----------------------------------------------------------------------------
void Frame::SetMinimizeButtonVisible(bool state)
{
#if !defined( _X360 )
_minimizeButton->SetVisible(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the maximize button
//-----------------------------------------------------------------------------
void Frame::SetMaximizeButtonVisible(bool state)
{
#if !defined( _X360 )
_maximizeButton->SetVisible(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Toggles visibility of the minimize-to-systray icon (defaults to false)
//-----------------------------------------------------------------------------
void Frame::SetMinimizeToSysTrayButtonVisible(bool state)
{
#if !defined( _X360 )
_minimizeToSysTrayButton->SetVisible(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Toggle visibility of the close button
//-----------------------------------------------------------------------------
void Frame::SetCloseButtonVisible(bool state)
{
#if !defined( _X360 )
_closeButton->SetVisible(state);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: soaks up any remaining messages
//-----------------------------------------------------------------------------
void Frame::OnKeyCodeReleased(KeyCode code)
{
}
//-----------------------------------------------------------------------------
// Purpose: soaks up any remaining messages
//-----------------------------------------------------------------------------
void Frame::OnKeyFocusTicked()
{
}
//-----------------------------------------------------------------------------
// Purpose: Toggles window flash state on a timer
//-----------------------------------------------------------------------------
void Frame::InternalFlashWindow()
{
if (_flashWindow)
{
// toggle icon flashing
_nextFlashState = true;
surface()->FlashWindow(GetVPanel(), _nextFlashState);
_nextFlashState = !_nextFlashState;
PostMessage(this, new KeyValues("FlashWindow"), 1.8f);
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds the child to the focus nav group
//-----------------------------------------------------------------------------
void Frame::OnChildAdded(VPANEL child)
{
BaseClass::OnChildAdded(child);
}
//-----------------------------------------------------------------------------
// Purpose: Flash the window system tray button until the frame gets focus
//-----------------------------------------------------------------------------
void Frame::FlashWindow()
{
_flashWindow = true;
_nextFlashState = true;
InternalFlashWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Stops any window flashing
//-----------------------------------------------------------------------------
void Frame::FlashWindowStop()
{
surface()->FlashWindow(GetVPanel(), false);
_flashWindow = false;
}
//-----------------------------------------------------------------------------
// Purpose: load the control settings - should be done after all the children are added to the dialog
//-----------------------------------------------------------------------------
void Frame::LoadControlSettings( const char *dialogResourceName, const char *pathID, KeyValues *pPreloadedKeyValues, KeyValues *pConditions )
{
BaseClass::LoadControlSettings( dialogResourceName, pathID, pPreloadedKeyValues, pConditions );
// set the focus on the default control
Panel *defaultFocus = GetFocusNavGroup().GetDefaultPanel();
if (defaultFocus)
{
defaultFocus->RequestFocus();
}
}
//-----------------------------------------------------------------------------
// Purpose: Checks for ctrl+shift+b hits to enter build mode
// Activates any hotkeys / default buttons
// Swallows any unhandled input
//-----------------------------------------------------------------------------
void Frame::OnKeyCodeTyped(KeyCode code)
{
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));
if ( IsX360() )
{
vgui::Panel *pMap = FindChildByName( "ControllerMap" );
if ( pMap && pMap->IsKeyBoardInputEnabled() )
{
pMap->OnKeyCodeTyped( code );
return;
}
}
if ( ctrl && shift && alt && code == KEY_B)
{
// enable build mode
ActivateBuildMode();
}
else if (ctrl && shift && alt && code == KEY_R)
{
// reload the scheme
VPANEL top = surface()->GetEmbeddedPanel();
if (top)
{
// reload the data file
scheme()->ReloadSchemes();
Panel *panel = ipanel()->GetPanel(top, GetModuleName());
if (panel)
{
// make the top-level panel reload it's scheme, it will chain down to all the child panels
panel->InvalidateLayout(false, true);
}
}
}
else if (alt && code == KEY_F4)
{
// user has hit the close
PostMessage(this, new KeyValues("CloseFrameButtonPressed"));
}
else if (code == KEY_ENTER)
{
// check for a default button
VPANEL panel = GetFocusNavGroup().GetCurrentDefaultButton();
if (panel && ipanel()->IsVisible( panel ) && ipanel()->IsEnabled( panel ))
{
// Activate the button
PostMessage(panel, new KeyValues("Hotkey"));
}
}
else if ( code == KEY_ESCAPE &&
surface()->SupportsFeature(ISurface::ESCAPE_KEY) &&
input()->GetAppModalSurface() == GetVPanel() )
{
// ESC cancels, unless we're in the engine - in the engine ESC flips between the UI and the game
CloseModal();
}
// Usually don't chain back as Frames are the end of the line for key presses, unless
// m_bChainKeysToParent is set
else if ( m_bChainKeysToParent )
{
BaseClass::OnKeyCodeTyped( code );
}
else
{
input()->OnKeyCodeUnhandled( (int)code );
}
}
//-----------------------------------------------------------------------------
// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame
// Input : state -
//-----------------------------------------------------------------------------
void Frame::SetChainKeysToParent( bool state )
{
m_bChainKeysToParent = state;
}
//-----------------------------------------------------------------------------
// Purpose: If true, then OnKeyCodeTyped messages continue up past the Frame
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool Frame::CanChainKeysToParent() const
{
return m_bChainKeysToParent;
}
//-----------------------------------------------------------------------------
// Purpose: Checks for ctrl+shift+b hits to enter build mode
// Activates any hotkeys / default buttons
// Swallows any unhandled input
//-----------------------------------------------------------------------------
void Frame::OnKeyTyped(wchar_t unichar)
{
Panel *panel = GetFocusNavGroup().FindPanelByHotkey(unichar);
if (panel)
{
// tell the panel to Activate
PostMessage(panel, new KeyValues("Hotkey"));
}
}
//-----------------------------------------------------------------------------
// Purpose: sets all title bar controls
//-----------------------------------------------------------------------------
void Frame::SetTitleBarVisible( bool state )
{
_drawTitleBar = state;
SetMenuButtonVisible(state);
SetMinimizeButtonVisible(state);
SetMaximizeButtonVisible(state);
SetCloseButtonVisible(state);
}
//-----------------------------------------------------------------------------
// Purpose: sets the frame to delete itself on close
//-----------------------------------------------------------------------------
void Frame::SetDeleteSelfOnClose( bool state )
{
m_bDeleteSelfOnClose = state;
}
//-----------------------------------------------------------------------------
// Purpose: updates localized text
//-----------------------------------------------------------------------------
void Frame::OnDialogVariablesChanged( KeyValues *dialogVariables )
{
StringIndex_t index = _title->GetUnlocalizedTextSymbol();
if (index != INVALID_LOCALIZE_STRING_INDEX)
{
// reconstruct the string from the variables
wchar_t buf[1024];
g_pVGuiLocalize->ConstructString(buf, sizeof(buf), index, dialogVariables);
SetTitle(buf, true);
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles staying on screen when the screen size changes
//-----------------------------------------------------------------------------
void Frame::OnScreenSizeChanged(int iOldWide, int iOldTall)
{
BaseClass::OnScreenSizeChanged(iOldWide, iOldTall);
if (IsProportional())
return;
// make sure we're completely on screen
int iNewWide, iNewTall;
surface()->GetScreenSize(iNewWide, iNewTall);
int x, y, wide, tall;
GetBounds(x, y, wide, tall);
// make sure the bottom-right corner is on the screen first
if (x + wide > iNewWide)
{
x = iNewWide - wide;
}
if (y + tall > iNewTall)
{
y = iNewTall - tall;
}
// make sure the top-left is visible
x = max( 0, x );
y = max( 0, y );
// apply
SetPos(x, y);
}
//-----------------------------------------------------------------------------
// Purpose: For supporting thin caption bars
// Input : state -
//-----------------------------------------------------------------------------
void Frame::SetSmallCaption( bool state )
{
m_bSmallCaption = state;
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool Frame::IsSmallCaption() const
{
return m_bSmallCaption;
}
//-----------------------------------------------------------------------------
// Purpose: Static method to place a frame under the cursor
//-----------------------------------------------------------------------------
void Frame::PlaceUnderCursor( )
{
// get cursor position, this is local to this text edit window
int cursorX, cursorY;
input()->GetCursorPos( cursorX, cursorY );
// relayout the menu immediately so that we know it's size
InvalidateLayout(true);
int w, h;
GetSize( w, h );
// work out where the cursor is and therefore the best place to put the frame
int sw, sh;
surface()->GetScreenSize( sw, sh );
// Try to center it first
int x, y;
x = cursorX - ( w / 2 );
y = cursorY - ( h / 2 );
// Clamp to various sides
if ( x + w > sw )
{
x = sw - w;
}
if ( y + h > sh )
{
y = sh - h;
}
if ( x < 0 )
{
x = 0;
}
if ( y < 0 )
{
y = 0;
}
SetPos( x, y );
}