|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <vgui/IBorder.h>
#include <vgui/IInput.h>
#include <vgui/IPanel.h>
#include <vgui/IScheme.h>
#include <vgui/ISystem.h>
#include <vgui/IVGui.h>
#include <vgui/KeyCode.h>
#include <keyvalues.h>
#include <vgui/MouseCode.h>
#include <vgui/ISurface.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/Label.h>
#include <vgui_controls/PropertySheet.h>
#include <vgui_controls/ComboBox.h>
#include <vgui_controls/Panel.h>
#include <vgui_controls/ToolWindow.h>
#include <vgui_controls/TextImage.h>
#include <vgui_controls/ImagePanel.h>
#include <vgui_controls/PropertyPage.h>
#include "vgui_controls/AnimationController.h"
#include "tier1/fmtstr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#ifdef _PS3
//!!BUG!! "wcsicmp is unsupported on PS3"
#ifdef wcsicmp
#undef wcsicmp
#endif
#define wcsicmp wcscmp
#endif
using namespace vgui;
namespace vgui {
class ContextLabel : public Label { DECLARE_CLASS_SIMPLE( ContextLabel, Label ); public:
ContextLabel( Button *parent, char const *panelName, char const *text ): BaseClass( (Panel *)parent, panelName, text ), m_pTabButton( parent ) { SetBlockDragChaining( true ); }
virtual void OnMousePressed( MouseCode code ) { if ( m_pTabButton ) { m_pTabButton->FireActionSignal(); } }
virtual void OnMouseReleased( MouseCode code ) { BaseClass::OnMouseReleased( code );
if ( GetParent() ) { GetParent()->OnCommand( "ShowContextMenu" ); } }
virtual void ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
HFont marlett = pScheme->GetFont( "Marlett" ); SetFont( marlett ); SetTextInset( 0, 0 ); SetContentAlignment( Label::a_northwest );
if ( GetParent() ) { SetFgColor( pScheme->GetColor( "Button.TextColor", GetParent()->GetFgColor() ) ); SetBgColor( GetParent()->GetBgColor() ); } } private:
Button *m_pTabButton; };
//-----------------------------------------------------------------------------
// Purpose: Helper for drag drop
// Input : msglist -
// Output : static PropertySheet
//-----------------------------------------------------------------------------
static PropertySheet *IsDroppingSheet( CUtlVector< KeyValues * >& msglist ) { if ( msglist.Count() == 0 ) return NULL;
KeyValues *data = msglist[ 0 ]; PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); if ( sheet ) return sheet;
return NULL; }
//-----------------------------------------------------------------------------
// Purpose: A single tab
//-----------------------------------------------------------------------------
class PageTab : public Button { DECLARE_CLASS_SIMPLE( PageTab, Button );
private: bool _active; Color _textColor; Color _dimTextColor; int m_bMaxTabWidth; IBorder *m_pActiveBorder; IBorder *m_pNormalBorder; PropertySheet *m_pParent; Panel *m_pPage; ImagePanel *m_pImage; char *m_pszImageName; bool m_bShowContextLabel; bool m_bAttemptingDrop; ContextLabel *m_pContextLabel; long m_hoverActivatePageTime; long m_dropHoverTime; bool m_bDragDropStyle;
public: PageTab(PropertySheet *parent, const char *panelName, const char *text, char const *imageName, int maxTabWidth, Panel *page, bool showContextButton, long hoverActivatePageTime = -1 ) : Button( (Panel *)parent, panelName, text), m_pActiveBorder( NULL ), m_pNormalBorder( NULL ), m_pParent( parent ), m_pPage( page ), m_pImage( 0 ), m_pszImageName( 0 ), m_bShowContextLabel( showContextButton ), m_bAttemptingDrop( false ), m_hoverActivatePageTime( hoverActivatePageTime ), m_dropHoverTime( -1 ), m_bDragDropStyle( false ) { SetCommand(new KeyValues("TabPressed")); _active = false; m_bMaxTabWidth = maxTabWidth; SetDropEnabled( true ); SetDragEnabled( m_pParent->IsDraggableTab() ); if ( imageName ) { m_pImage = new ImagePanel( this, text ); int buflen = Q_strlen( imageName ) + 1; m_pszImageName = new char[ buflen ]; Q_strncpy( m_pszImageName, imageName, buflen );
} SetMouseClickEnabled( MOUSE_RIGHT, true ); m_pContextLabel = m_bShowContextLabel ? new ContextLabel( this, "Context", "9" ) : NULL;
REGISTER_COLOR_AS_OVERRIDABLE( _textColor, "selectedcolor" ); REGISTER_COLOR_AS_OVERRIDABLE( _dimTextColor, "unselectedcolor" ); }
~PageTab() { delete[] m_pszImageName; }
void SetDragDropStyle( bool bStyle ) { m_bDragDropStyle = bStyle; }
virtual void Paint() { BaseClass::Paint(); if ( !m_bDragDropStyle ) return; int w, h; GetSize( w, h ); surface()->DrawSetColor( m_pParent->GetDropFrameColor() ); // Top
surface()->DrawOutlinedRect( 0, 0, w, 2 ); // surface()->DrawOutlinedRect( 0, 0, w, 1 );
// Left
surface()->DrawOutlinedRect( 0, 0, 2, h ); // Right
surface()->DrawOutlinedRect( w-2, 0, w, h ); }
virtual void OnCursorEntered() { m_dropHoverTime = system()->GetTimeMillis(); }
virtual void OnCursorExited() { m_dropHoverTime = -1; }
virtual void OnThink() { if ( m_bAttemptingDrop && m_hoverActivatePageTime >= 0 && m_dropHoverTime >= 0 ) { long hoverTime = system()->GetTimeMillis() - m_dropHoverTime; if ( hoverTime > m_hoverActivatePageTime ) { FireActionSignal(); SetSelected(true); Repaint(); } } m_bAttemptingDrop = false;
BaseClass::OnThink(); }
virtual bool IsDroppable( CUtlVector< KeyValues * >&msglist ) { m_bAttemptingDrop = true;
if ( !GetParent() ) return false;
PropertySheet *sheet = IsDroppingSheet( msglist ); if ( sheet ) return GetParent()->IsDroppable( msglist );
return BaseClass::IsDroppable( msglist ); }
virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) { PropertySheet *sheet = IsDroppingSheet( msglist ); if ( sheet ) { Panel *target = GetParent()->GetDropTarget( msglist ); if ( target ) { // Fixme, mouse pos could be wrong...
target->OnDroppablePanelPaint( msglist, dragPanels ); return; } }
// Just highlight the tab if dropping onto active page via the tab
BaseClass::OnDroppablePanelPaint( msglist, dragPanels ); }
virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ) { PropertySheet *sheet = IsDroppingSheet( msglist );
// Msg( "Tab::OnPanelDropped(sheet %s)\n", sheet ? "yes" : "no" );
if ( sheet ) { Panel *target = GetParent()->GetDropTarget( msglist ); if ( target ) { // Fixme, mouse pos could be wrong...
target->OnPanelDropped( msglist ); } }
// Defer to active page...
Panel *active = m_pParent->GetActivePage(); if ( !active || !active->IsDroppable( msglist ) ) return;
active->OnPanelDropped( msglist ); }
virtual void OnDragFailed( CUtlVector< KeyValues * >& msglist ) { PropertySheet *sheet = IsDroppingSheet( msglist ); if ( !sheet ) return;
// Msg( "Tab::OnDragFailed(sheet %s)\n", sheet ? "yes" : "no" );
// Create a new property sheet
if ( m_pParent->IsDraggableTab() ) { if ( msglist.Count() == 1 ) { KeyValues *data = msglist[ 0 ]; int screenx = data->GetInt( "screenx" ); int screeny = data->GetInt( "screeny" );
// m_pParent->ScreenToLocal( screenx, screeny );
if ( !m_pParent->IsWithin( screenx, screeny ) ) { Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); PropertySheet *sheet = reinterpret_cast< PropertySheet * >( data->GetPtr( "propertysheet" ) ); char const *title = data->GetString( "tabname", "" ); if ( !page || !sheet ) return; // Can only create if sheet was part of a ToolWindow derived object
ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); if ( tw ) { IToolWindowFactory *factory = tw->GetToolWindowFactory(); if ( factory ) { bool hasContextMenu = sheet->PageHasContextMenu( page ); sheet->RemovePage( page ); factory->InstanceToolWindow( tw->GetParent(), sheet->ShouldShowContextButtons(), page, title, hasContextMenu );
if ( sheet->GetNumPages() == 0 ) { tw->MarkForDeletion(); } } } } } } }
virtual HCursor GetDragFailCursor( CUtlVector< KeyValues * >& msglist ) { PropertySheet *sheet = IsDroppingSheet( msglist ); if ( !sheet ) return BaseClass::GetDragFailCursor( msglist );
// Dragging a pagetab somewhere invalid will result in a new toolwindow getting created, which is perfectly valid
return dc_arrow; }
virtual void OnCreateDragData( KeyValues *msg ) { Assert( m_pParent->IsDraggableTab() );
msg->SetPtr( "propertypage", m_pPage ); msg->SetPtr( "propertysheet", m_pParent ); char sz[ 256 ]; GetText( sz, sizeof( sz ) ); msg->SetString( "tabname", sz ); msg->SetString( "text", sz ); }
virtual void ApplySchemeSettings(IScheme *pScheme) { // set up the scheme settings
Button::ApplySchemeSettings(pScheme);
_textColor = GetSchemeColor("PropertySheet.SelectedTextColor", GetFgColor(), pScheme); _dimTextColor = GetSchemeColor("PropertySheet.TextColor", GetFgColor(), pScheme); m_pActiveBorder = pScheme->GetBorder("TabActiveBorder"); m_pNormalBorder = pScheme->GetBorder("TabBorder");
Resize();
if ( m_pContextLabel ) { SetTextInset( 12, 0 ); } }
void Resize() { if ( m_pImage ) { ClearImages(); m_pImage->SetImage(scheme()->GetImage(m_pszImageName, false)); AddImage( m_pImage->GetImage(), 2 ); int w, h; m_pImage->GetSize( w, h ); w += m_pContextLabel ? 10 : 0; if ( m_pContextLabel ) { m_pImage->SetPos( 10, 0 ); } SetSize( w + 4, h + 2 ); } else { int wide, tall; int contentWide, contentTall; GetSize(wide, tall); GetContentSize(contentWide, contentTall);
wide = MAX(m_bMaxTabWidth, contentWide + 10); // 10 = 5 pixels margin on each side
wide += m_pContextLabel ? 10 : 0; SetSize(wide, tall); } }
virtual void ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings(inResourceData);
const char *pBorder = inResourceData->GetString("activeborder_override", ""); if (*pBorder) { m_pActiveBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); } pBorder = inResourceData->GetString("normalborder_override", ""); if (*pBorder) { m_pNormalBorder = scheme()->GetIScheme(GetScheme())->GetBorder( pBorder ); } }
virtual void OnCommand( char const *cmd ) { if ( !Q_stricmp( cmd, "ShowContextMenu" ) ) { KeyValues *kv = new KeyValues("OpenContextMenu"); kv->SetPtr( "page", m_pPage ); kv->SetPtr( "contextlabel", m_pContextLabel ); PostActionSignal( kv ); return; } BaseClass::OnCommand( cmd ); }
IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus) { if (_active) { return m_pActiveBorder; } return m_pNormalBorder; }
virtual Color GetButtonFgColor() { if (_active) { return _textColor; } else { return _dimTextColor; } }
virtual void SetActive(bool state) { _active = state; SetZPos( state ? 100 : 0 ); InvalidateLayout(); Repaint(); }
virtual void SetTabWidth( int iWidth ) { m_bMaxTabWidth = iWidth; InvalidateLayout(); }
virtual bool CanBeDefaultButton(void) { return false; }
//Fire action signal when mouse is pressed down instead of on release.
virtual void OnMousePressed(MouseCode code) { // check for context menu open
if (!IsEnabled()) return; if (!IsMouseClickEnabled(code)) return; if (IsUseCaptureMouseEnabled()) { { RequestFocus(); FireActionSignal(); SetSelected(true); Repaint(); } // lock mouse input to going to this button
input()->SetMouseCapture(GetVPanel()); } }
virtual void OnMouseReleased(MouseCode code) { // ensure mouse capture gets released
if (IsUseCaptureMouseEnabled()) { input()->SetMouseCapture(NULL); }
// make sure the button gets unselected
SetSelected(false); Repaint();
if (code == MOUSE_RIGHT) { KeyValues *kv = new KeyValues("OpenContextMenu"); kv->SetPtr( "page", m_pPage ); kv->SetPtr( "contextlabel", m_pContextLabel ); PostActionSignal( kv ); } }
virtual void PerformLayout() { BaseClass::PerformLayout();
if ( m_pContextLabel ) { int w, h; GetSize( w, h ); m_pContextLabel->SetBounds( 0, 0, 10, h ); } } };
}; // namespace vgui
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
PropertySheet::PropertySheet( Panel *parent, const char *panelName, bool draggableTabs /*= false*/ ) : BaseClass(parent, panelName) { _activePage = NULL; _activeTab = NULL; _tabWidth = 64; _activeTabIndex = 0; _showTabs = true; _combo = NULL; _tabFocus = false; m_flPageTransitionEffectTime = 0.0f; m_bSmallTabs = false; m_tabFont = 0; m_bDraggableTabs = draggableTabs; m_pTabKV = NULL; m_iTabHeight = 0; m_iTabHeightSmall = 0;
if ( m_bDraggableTabs ) { SetDropEnabled( true ); }
m_bKBNavigationEnabled = true; m_pDragDropTab = new PageTab( this, "dragdroptab", "", NULL, _tabWidth, NULL, false, 0 ); m_pDragDropTab->SetVisible( false ); m_pDragDropTab->SetDragDropStyle( true ); m_nPageDropTabVisibleTime = -1; m_pTemporarilyRemoved = NULL; }
//-----------------------------------------------------------------------------
// Purpose: Constructor, associates pages with a combo box
//-----------------------------------------------------------------------------
PropertySheet::PropertySheet(Panel *parent, const char *panelName, ComboBox *combo) : BaseClass(parent, panelName) { _activePage = NULL; _activeTab = NULL; _tabWidth = 64; _activeTabIndex = 0; _combo=combo; _combo->AddActionSignalTarget(this); _showTabs = false; _tabFocus = false; m_flPageTransitionEffectTime = 0.0f; m_bSmallTabs = false; m_tabFont = 0; m_bDraggableTabs = false; m_pTabKV = NULL; m_pDragDropTab = new PageTab( this, "dragdroptab", "", NULL, _tabWidth, NULL, false, 0 ); m_pDragDropTab->SetVisible( false ); m_pDragDropTab->SetDragDropStyle( true ); m_nPageDropTabVisibleTime = -1; m_pTemporarilyRemoved = NULL; m_iTabHeight = 0; m_iTabHeightSmall = 0; }
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
PropertySheet::~PropertySheet() { }
//-----------------------------------------------------------------------------
// Purpose: ToolWindow uses this to drag tools from container to container by dragging the tab
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool PropertySheet::IsDraggableTab() const { return m_bDraggableTabs; }
void PropertySheet::SetDraggableTabs( bool state ) { m_bDraggableTabs = state; }
//-----------------------------------------------------------------------------
// Purpose: Lower profile tabs
// Input : state -
//-----------------------------------------------------------------------------
void PropertySheet::SetSmallTabs( bool state ) { m_bSmallTabs = state; m_tabFont = scheme()->GetIScheme( GetScheme() )->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" ); int c = m_PageTabs.Count(); for ( int i = 0; i < c ; ++i ) { PageTab *tab = m_PageTabs[ i ]; Assert( tab ); tab->SetFont( m_tabFont ); } }
void PropertySheet::SetShowTabs( bool state ) { _showTabs = state; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool PropertySheet::IsSmallTabs() const { return m_bSmallTabs; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : state -
//-----------------------------------------------------------------------------
void PropertySheet::ShowContextButtons( bool state ) { m_bContextButton = state; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool PropertySheet::ShouldShowContextButtons() const { return m_bContextButton; }
int PropertySheet::FindPage( Panel *page ) const { int c = m_Pages.Count(); for ( int i = 0; i < c; ++i ) { if ( m_Pages[ i ].page == page ) return i; }
return m_Pages.InvalidIndex(); }
void PropertySheet::SetPageTitle( Panel *page, const char *title ) { if ( !page ) return;
int nSlot = FindPage( page ); if ( nSlot == m_Pages.InvalidIndex() ) return;
PageTab *pTab = m_PageTabs[ nSlot ]; pTab->SetText( title ); }
//-----------------------------------------------------------------------------
// Purpose: adds a page to the sheet
//-----------------------------------------------------------------------------
void PropertySheet::AddPage(Panel *page, const char *title, char const *imageName /*= NULL*/, bool bHasContextMenu /*= false*/, int nInsertBefore /*= -1*/ ) { if (!page) return;
// don't add the page if we already have it
int nSlot = FindPage( page ); if ( nSlot != m_Pages.InvalidIndex() ) { // See about re-ordering
if ( nInsertBefore != -1 && ( nSlot != nInsertBefore ) ) { if ( nSlot < nInsertBefore ) { --nInsertBefore; }
PageTab *pTab = m_PageTabs[ nSlot ]; m_PageTabs.Remove( nSlot ); Page_t pt = m_Pages[ nSlot ]; m_Pages.Remove( nSlot ); m_PageTabs.InsertBefore( nInsertBefore, pTab ); m_Pages.InsertBefore( nInsertBefore, pt );
InvalidateLayout(); } return; }
long hoverActivatePageTime = 250; PageTab *tab = new PageTab(this, CFmtStr( "tab_%s", title ), title, imageName, _tabWidth, page, m_bContextButton && bHasContextMenu, hoverActivatePageTime ); if ( m_bDraggableTabs ) { tab->SetDragEnabled( true ); }
tab->SetFont( m_tabFont ); if(_showTabs) { tab->AddActionSignalTarget(this); } else if (_combo) { _combo->AddItem(title, NULL); }
if ( m_pTabKV ) { tab->ApplySettings( m_pTabKV ); }
if ( nInsertBefore == -1 ) { nInsertBefore = m_PageTabs.Count(); }
m_PageTabs.InsertBefore( nInsertBefore, tab );
Page_t info; info.page = page; info.contextMenu = m_bContextButton && bHasContextMenu; m_Pages.InsertBefore( nInsertBefore, info );
page->SetParent(this); page->AddActionSignalTarget(this); PostMessage(page, new KeyValues("ResetData"));
page->SetVisible(false); InvalidateLayout();
if (!_activePage) { // first page becomes the active page
ChangeActiveTab( 0 ); if ( _activePage ) { _activePage->RequestFocus( 0 ); } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::SetActivePage(Panel *page) { // walk the list looking for this page
int index = FindPage( page ); if (!m_Pages.IsValidIndex(index)) return;
ChangeActiveTab(index); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::SetTabWidth(int pixels) { _tabWidth = pixels;
// Update page tabs with the new width
int m = m_PageTabs.Count(); for (int i=0;i<m;i++) { m_PageTabs[i]->SetTabWidth( pixels ); }
InvalidateLayout(); }
//-----------------------------------------------------------------------------
// Purpose: reloads the data in all the property page
//-----------------------------------------------------------------------------
void PropertySheet::ResetAllData() { // iterate all the dialogs resetting them
for (int i = 0; i < m_Pages.Count(); i++) { ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ResetData"), GetVPanel()); } }
//-----------------------------------------------------------------------------
// Purpose: Applies any changes made by the dialog
//-----------------------------------------------------------------------------
void PropertySheet::ApplyChanges() { // iterate all the dialogs resetting them
for (int i = 0; i < m_Pages.Count(); i++) { ipanel()->SendMessage(m_Pages[i].page->GetVPanel(), new KeyValues("ApplyChanges"), GetVPanel()); } }
//-----------------------------------------------------------------------------
// Purpose: gets a pointer to the currently active page
//-----------------------------------------------------------------------------
Panel *PropertySheet::GetActivePage() { return _activePage; }
//-----------------------------------------------------------------------------
// Purpose: gets a pointer to the currently active tab
//-----------------------------------------------------------------------------
Panel *PropertySheet::GetActiveTab() { return _activeTab; }
//-----------------------------------------------------------------------------
// Purpose: gets a pointer tab "i"
//-----------------------------------------------------------------------------
Panel* PropertySheet::GetTab(int i) { if (i < 0 && i > m_PageTabs.Count()) { return NULL; } return m_PageTabs[i]; }
//-----------------------------------------------------------------------------
// Purpose: returns the number of panels in the sheet
//-----------------------------------------------------------------------------
int PropertySheet::GetNumPages() { return m_Pages.Count(); }
//-----------------------------------------------------------------------------
// Purpose: returns the name contained in the active tab
// Input : a text buffer to contain the output
//-----------------------------------------------------------------------------
void PropertySheet::GetActiveTabTitle(char *textOut, int bufferLen) { if(_activeTab) _activeTab->GetText(textOut, bufferLen); }
//-----------------------------------------------------------------------------
// Purpose: returns the name contained in the active tab
// Input : a text buffer to contain the output
//-----------------------------------------------------------------------------
bool PropertySheet::GetTabTitle(int i, char *textOut, int bufferLen) { if (i < 0 && i > m_PageTabs.Count()) { return false; }
m_PageTabs[i]->GetText(textOut, bufferLen); return true; }
//-----------------------------------------------------------------------------
// Purpose: Returns the index of the currently active page
//-----------------------------------------------------------------------------
int PropertySheet::GetActivePageNum() { for (int i = 0; i < m_Pages.Count(); i++) { if (m_Pages[i].page == _activePage) { return i; } } return -1; }
//-----------------------------------------------------------------------------
// Purpose: Forwards focus requests to current active page
//-----------------------------------------------------------------------------
void PropertySheet::RequestFocus(int direction) { if (direction == -1 || direction == 0) { if (_activePage) { _activePage->RequestFocus(direction); _tabFocus = false; } } else { if (_showTabs && _activeTab) { _activeTab->RequestFocus(direction); _tabFocus = true; } else if (_activePage) { _activePage->RequestFocus(direction); _tabFocus = false; } } }
//-----------------------------------------------------------------------------
// Purpose: moves focus back
//-----------------------------------------------------------------------------
bool PropertySheet::RequestFocusPrev(VPANEL panel) { if (_tabFocus || !_showTabs || !_activeTab) { _tabFocus = false; return BaseClass::RequestFocusPrev(panel); } else { if (GetVParent()) { PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); } _activeTab->RequestFocus(-1); _tabFocus = true; return true; } }
//-----------------------------------------------------------------------------
// Purpose: moves focus forward
//-----------------------------------------------------------------------------
bool PropertySheet::RequestFocusNext(VPANEL panel) { if (!_tabFocus || !_activePage) { return BaseClass::RequestFocusNext(panel); } else { if (!_activeTab) { return BaseClass::RequestFocusNext(panel); } else { _activePage->RequestFocus(1); _tabFocus = false; return true; } } }
//-----------------------------------------------------------------------------
// Purpose: Gets scheme settings
//-----------------------------------------------------------------------------
void PropertySheet::ApplySchemeSettings(IScheme *pScheme) { // Hack Hack: This resets m_iTabHeight and m_iTabHeightSmall as if we just initialized the panel
InternalInitDefaultValues( GetAnimMap() );
BaseClass::ApplySchemeSettings(pScheme);
// a little backwards-compatibility with old scheme files
IBorder *pBorder = pScheme->GetBorder("PropertySheetBorder"); if (pBorder == pScheme->GetBorder("Default")) { // get the old name
pBorder = pScheme->GetBorder("RaisedBorder"); }
SetBorder(pBorder); m_flPageTransitionEffectTime = atof(pScheme->GetResourceString("PropertySheet.TransitionEffectTime"));
m_tabFont = pScheme->GetFont( m_bSmallTabs ? "DefaultVerySmall" : "Default" );
if ( m_pTabKV ) { for (int i = 0; i < m_PageTabs.Count(); i++) { m_PageTabs[i]->ApplySettings( m_pTabKV ); } }
//=============================================================================
// HPE_BEGIN:
// [tj] Here, we used to use a single size variable and overwrite it when we scaled.
// This led to problems when we changes resolutions, so now we recalcuate the absolute
// size from the relative size each time (based on proportionality)
//=============================================================================
if ( IsProportional() ) { m_iTabHeight = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeight ); m_iTabHeightSmall = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iSpecifiedTabHeightSmall ); } else { m_iTabHeight = m_iSpecifiedTabHeight; m_iTabHeightSmall = m_iSpecifiedTabHeightSmall; } //=============================================================================
// HPE_END
//=============================================================================
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::ApplySettings(KeyValues *inResourceData) { BaseClass::ApplySettings(inResourceData);
KeyValues *pTabKV = inResourceData->FindKey( "tabskv" ); if ( pTabKV ) { if ( m_pTabKV ) { m_pTabKV->deleteThis(); } m_pTabKV = new KeyValues("tabkv"); pTabKV->CopySubkeys( m_pTabKV ); }
KeyValues *pTabWidthKV = inResourceData->FindKey( "tabwidth" ); if ( pTabWidthKV ) { _tabWidth = scheme()->GetProportionalScaledValueEx(GetScheme(), pTabWidthKV->GetInt()); for (int i = 0; i < m_PageTabs.Count(); i++) { m_PageTabs[i]->SetTabWidth( _tabWidth ); } }
KeyValues *pTransitionKV = inResourceData->FindKey( "transition_time" ); if ( pTransitionKV ) { m_flPageTransitionEffectTime = pTransitionKV->GetFloat(); } }
//-----------------------------------------------------------------------------
// Purpose: Paint our border specially, with the tabs in mind
//-----------------------------------------------------------------------------
void PropertySheet::PaintBorder() { IBorder *border = GetBorder(); if (!border) return;
PageTab *pActive = _activeTab; if ( m_PageTabs.Find( m_pDragDropTab ) != m_PageTabs.InvalidIndex() ) { pActive = m_pDragDropTab; }
// draw the border, but with a break at the active tab
int px = 0, py = 0, pwide = 0, ptall = 0; if (pActive) { pActive->GetBounds(px, py, pwide, ptall); ptall -= 1; }
// draw the border underneath the buttons, with a break
int wide, tall; GetSize(wide, tall); border->Paint(0, py + ptall, wide, tall, IBorder::SIDE_TOP, px + 1, px + pwide - 1); }
// Grabs mouse coords and figures out best tab to insert "before" or -1 if insert should be at end of tab section row
PageTab *PropertySheet::FindInsertBeforeTab() { int mx, my; input()->GetCursorPos( mx, my ); ScreenToLocal( mx, my );
int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; if ( my < 0 || my > tabHeight ) return NULL;
// Walk through existing tabs
int c = m_PageTabs.Count(); int nPrevX = 0; for ( int i = 0; i < c; ++i ) { if ( m_PageTabs[ i ] == m_pTemporarilyRemoved ) continue;
int bounds[ 4 ]; m_PageTabs[ i ]->GetBounds( bounds[ 0 ], bounds[ 1 ], bounds[ 2 ], bounds[ 3 ] );
int nMidX = bounds[ 0 ] + ( bounds[ 2 ] * 0.5f );
if ( mx >= nPrevX && mx <= nMidX ) { if ( m_PageTabs[ i ] == m_pDragDropTab ) { if ( i < c - 1 ) { return m_PageTabs[ i + 1 ]; } else { return NULL; } } return m_PageTabs[ i ]; }
nPrevX = nMidX; }
return NULL; }
void PropertySheet::LayoutTabs() { int x, y, wide, tall; GetBounds(x, y, wide, tall);
int xtab; int limit = m_PageTabs.Count();
xtab = m_iTabXIndent;
PageTab *pActive = _activeTab; if ( m_PageTabs.Find( m_pDragDropTab ) != m_PageTabs.InvalidIndex() ) { pActive = m_pDragDropTab; }
// draw the visible tabs
if ( _showTabs ) { int tabHeight = IsSmallTabs() ? (m_iTabHeightSmall-1) : (m_iTabHeight-1);
for (int i = 0; i < limit; i++) { if ( m_pTemporarilyRemoved == m_PageTabs[ i ] ) { continue; } int width, tall; m_PageTabs[i]->GetSize(width, tall);
if ( m_bTabFitText ) { m_PageTabs[i]->SizeToContents(); width = m_PageTabs[i]->GetWide();
int iXInset, iYInset; m_PageTabs[i]->GetTextInset( &iXInset, &iYInset ); width += (iXInset * 2); } if (m_PageTabs[i] == pActive) { // active tab is taller
pActive->SetBounds(xtab, 2, width, tabHeight); } else { m_PageTabs[i]->SetBounds(xtab, 4, width, tabHeight - 2); } m_PageTabs[i]->SetVisible(true); xtab += (width + 1) + m_iTabXDelta; } } else { for (int i = 0; i < limit; i++) { m_PageTabs[i]->SetVisible(false); } }
if ( _activeTab ) { _activeTab->MoveToFront(); _activeTab->Repaint(); } }
//-----------------------------------------------------------------------------
// Purpose: Lays out the dialog
//-----------------------------------------------------------------------------
void PropertySheet::PerformLayout() { BaseClass::PerformLayout();
int x, y, wide, tall; GetBounds(x, y, wide, tall); for ( int i = 0; i < m_Pages.Count(); ++ i ) { if ( m_Pages[i].page == _activePage ) { int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight;
if(_showTabs) { _activePage->SetBounds(0, tabHeight, wide, tall - tabHeight); } else { _activePage->SetBounds(0, 0, wide, tall ); } _activePage->InvalidateLayout(); // ensure draw order (page drawing over all the tabs except one)
_activePage->MoveToFront(); _activePage->Repaint(); } else { m_Pages[i].page->SetVisible( false ); } }
LayoutTabs(); }
//-----------------------------------------------------------------------------
// Purpose: Switches the active panel
//-----------------------------------------------------------------------------
void PropertySheet::OnTabPressed(Panel *panel) { // look for the tab in the list
for (int i = 0; i < m_PageTabs.Count(); i++) { if (m_PageTabs[i] == panel) { // flip to the new tab
ChangeActiveTab(i); return; } } }
//-----------------------------------------------------------------------------
// Purpose: returns the panel associated with index i
// Input : the index of the panel to return
//-----------------------------------------------------------------------------
Panel *PropertySheet::GetPage(int i) { if( i < 0 || i >= m_Pages.Count() ) { return NULL; }
return m_Pages[i].page; }
//-----------------------------------------------------------------------------
// Purpose: disables page by name
//-----------------------------------------------------------------------------
void PropertySheet::DisablePage(const char *title) { SetPageEnabled(title, false); }
//-----------------------------------------------------------------------------
// Purpose: enables page by name
//-----------------------------------------------------------------------------
void PropertySheet::EnablePage(const char *title) { SetPageEnabled(title, true); }
//-----------------------------------------------------------------------------
// Purpose: enabled or disables page by name
//-----------------------------------------------------------------------------
void PropertySheet::SetPageEnabled(const char *title, bool state) { for (int i = 0; i < m_PageTabs.Count(); i++) { if (_showTabs) { char tmp[50]; m_PageTabs[i]->GetText(tmp,50); if (!strnicmp(title,tmp,strlen(tmp))) { m_PageTabs[i]->SetEnabled(state); } } else if ( _combo ) { _combo->SetItemEnabled(title,state); } } }
void PropertySheet::RemoveAllPages() { int c = m_Pages.Count(); for ( int i = c - 1; i >= 0 ; --i ) { RemovePage( m_Pages[ i ].page ); } }
void PropertySheet::DeleteAllPages() { int c = m_Pages.Count(); for ( int i = c - 1; i >= 0 ; --i ) { DeletePage( m_Pages[ i ].page ); } }
//-----------------------------------------------------------------------------
// Purpose: deletes the page associated with panel
// Input : *panel - the panel of the page to remove
//-----------------------------------------------------------------------------
void PropertySheet::RemovePage(Panel *panel) { int location = FindPage( panel ); if ( location == m_Pages.InvalidIndex() ) return;
// Since it's being deleted, don't animate!!!
m_hPreviouslyActivePage = NULL; _activeTab = NULL;
// ASSUMPTION = that the number of pages equals number of tabs
if( _showTabs ) { m_PageTabs[location]->RemoveActionSignalTarget( this ); } // now remove the tab
PageTab *tab = m_PageTabs[ location ]; m_PageTabs.Remove( location ); tab->MarkForDeletion(); // Remove from page list
m_Pages.Remove( location );
// Unparent
panel->SetParent( (Panel *)NULL );
if ( _activePage == panel ) { _activePage = NULL; // if this page is currently active, backup to the page before this.
ChangeActiveTab( MAX( location - 1, 0 ) ); }
PerformLayout(); }
//-----------------------------------------------------------------------------
// Purpose: deletes the page associated with panel
// Input : *panel - the panel of the page to remove
//-----------------------------------------------------------------------------
void PropertySheet::DeletePage(Panel *panel) { Assert( panel ); RemovePage( panel ); panel->MarkForDeletion(); }
//-----------------------------------------------------------------------------
// Purpose: flips to the new tab, sending out all the right notifications
// flipping to a tab activates the tab.
//-----------------------------------------------------------------------------
void PropertySheet::ChangeActiveTab( int index ) { if ( !m_Pages.IsValidIndex( index ) ) { _activeTab = NULL; if ( m_Pages.Count() > 0 ) { _activePage = NULL; ChangeActiveTab( 0 ); } return; }
if ( m_Pages[index].page == _activePage ) { if ( _activeTab ) { _activeTab->RequestFocus(); } _tabFocus = true; return; }
int c = m_Pages.Count(); for ( int i = 0; i < c; ++i ) { m_Pages[ i ].page->SetVisible( false ); }
m_hPreviouslyActivePage = _activePage;
// We're going to fade out the active page, so don't hide it yet
if (_activePage && m_flPageTransitionEffectTime) { _activePage->SetVisible(true); }
// notify old page
if (_activePage) { ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageHide"), GetVPanel()); KeyValues *msg = new KeyValues("PageTabActivated"); msg->SetPtr("panel", (Panel *)NULL); ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel()); } if (_activeTab) { //_activeTabIndex=index;
_activeTab->SetActive(false);
// does the old tab have the focus?
_tabFocus = _activeTab->HasFocus(); } else { _tabFocus = false; }
// flip page
_activePage = m_Pages[index].page; _activeTab = m_PageTabs[index]; _activeTabIndex = index;
_activePage->SetVisible(true); _activePage->MoveToFront(); _activeTab->SetVisible(true); _activeTab->MoveToFront(); _activeTab->SetActive(true);
if (_tabFocus) { // if a tab already has focused,give the new tab the focus
_activeTab->RequestFocus(); } else { // otherwise, give the focus to the page
_activePage->RequestFocus(); }
if (!_showTabs && _combo) { _combo->ActivateItemByRow(index); }
_activePage->MakeReadyForUse();
// transition effect
if (m_flPageTransitionEffectTime) { if (m_hPreviouslyActivePage.Get()) { // fade out the previous page
GetAnimationController()->RunAnimationCommand(m_hPreviouslyActivePage, "Alpha", 0.0f, 0.0f, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); }
// fade in the new page
_activePage->SetAlpha(0); GetAnimationController()->RunAnimationCommand(_activePage, "Alpha", 255.0f, m_flPageTransitionEffectTime / 2, m_flPageTransitionEffectTime / 2, AnimationController::INTERPOLATOR_LINEAR); } else { if (m_hPreviouslyActivePage.Get()) { // no transition, just hide the previous page
m_hPreviouslyActivePage->SetVisible(false); } _activePage->SetAlpha( 255 ); }
// notify
ivgui()->PostMessage(_activePage->GetVPanel(), new KeyValues("PageShow"), GetVPanel());
KeyValues *msg = new KeyValues("PageTabActivated"); msg->SetPtr("panel", (Panel *)_activeTab); ivgui()->PostMessage(_activePage->GetVPanel(), msg, GetVPanel());
// tell parent
PostActionSignal(new KeyValues("PageChanged"));
// Repaint
InvalidateLayout(); Repaint(); }
//-----------------------------------------------------------------------------
// Purpose: Gets the panel with the specified hotkey, from the current page
//-----------------------------------------------------------------------------
Panel *PropertySheet::HasHotkey(wchar_t key) { if (!_activePage) return NULL;
for (int i = 0; i < _activePage->GetChildCount(); i++) { Panel *hot = _activePage->GetChild(i)->HasHotkey(key); if (hot) { return hot; } } return NULL; }
//-----------------------------------------------------------------------------
// Purpose: catches the opencontextmenu event
//-----------------------------------------------------------------------------
void PropertySheet::OnOpenContextMenu( KeyValues *params ) { // tell parent
KeyValues *kv = params->MakeCopy(); PostActionSignal( kv ); Panel *page = reinterpret_cast< Panel * >( params->GetPtr( "page" ) ); if ( page ) { PostMessage( page->GetVPanel(), params->MakeCopy() ); } }
//-----------------------------------------------------------------------------
// Purpose: Handle key presses, through tabs.
//-----------------------------------------------------------------------------
void PropertySheet::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 ( ctrl && shift && alt && code == KEY_B) { // enable build mode
EditablePanel *ep = dynamic_cast< EditablePanel * >( GetActivePage() ); if ( ep ) { ep->ActivateBuildMode(); return; } }
if ( IsKBNavigationEnabled() ) { switch (code) { // for now left and right arrows just open or close submenus if they are there.
case KEY_RIGHT: { ChangeActiveTab(_activeTabIndex+1); break; } case KEY_LEFT: { ChangeActiveTab(_activeTabIndex-1); break; } default: BaseClass::OnKeyCodeTyped(code); break; } } else { BaseClass::OnKeyCodeTyped(code); } }
//-----------------------------------------------------------------------------
// Purpose: Called by the associated combo box (if in that mode), changes the current panel
//-----------------------------------------------------------------------------
void PropertySheet::OnTextChanged(Panel *panel,const wchar_t *wszText) { if ( panel == _combo ) { wchar_t tabText[30]; for(int i = 0 ; i < m_PageTabs.Count() ; i++ ) { tabText[0] = 0; m_PageTabs[i]->GetText(tabText,30); if ( !wcsicmp(wszText,tabText) ) { ChangeActiveTab(i); } } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::OnCommand(const char *command) { // propogate the close command to our parent
if (!stricmp(command, "Close") && GetVParent()) { CallParentFunction(new KeyValues("Command", "command", command)); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::OnApplyButtonEnable() { // tell parent
PostActionSignal(new KeyValues("ApplyButtonEnable")); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::OnCurrentDefaultButtonSet( vgui::VPANEL defaultButton ) { // forward the message up
if (GetVParent()) { KeyValues *msg = new KeyValues("CurrentDefaultButtonSet"); msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); PostMessage(GetVParent(), msg); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::OnDefaultButtonSet( VPANEL defaultButton ) { // forward the message up
if (GetVParent()) { KeyValues *msg = new KeyValues("DefaultButtonSet"); msg->SetInt("button", ivgui()->PanelToHandle( defaultButton ) ); PostMessage(GetVParent(), msg); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void PropertySheet::OnFindDefaultButton() { if (GetVParent()) { PostMessage(GetVParent(), new KeyValues("FindDefaultButton")); } }
bool PropertySheet::PageHasContextMenu( Panel *page ) const { int pageNum = FindPage( page ); if ( pageNum == m_Pages.InvalidIndex() ) return false;
return m_Pages[ pageNum ].contextMenu; }
void PropertySheet::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) { PageTab *pBefore = FindInsertBeforeTab();
ClearPageDropTab();
if ( msglist.Count() != 1 ) { return; }
PropertySheet *sheet = IsDroppingSheet( msglist );
// Msg( "PropertySheet::OnPanelDropped(sheet %s)\n", sheet ? "yes" : "no" );
if ( !sheet ) { // Defer to active page
if ( _activePage && _activePage->IsDropEnabled() ) { _activePage->OnPanelDropped( msglist ); } return; }
KeyValues *data = msglist[ 0 ];
Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); char const *title = data->GetString( "tabname", "" ); if ( !page || !sheet ) return;
// Can only create if sheet was part of a ToolWindow derived object
ToolWindow *tw = dynamic_cast< ToolWindow * >( sheet->GetParent() ); if ( tw ) { IToolWindowFactory *factory = tw->GetToolWindowFactory(); if ( factory ) { bool showContext = sheet->PageHasContextMenu( page ); sheet->RemovePage( page ); if ( sheet->GetNumPages() == 0 ) { tw->MarkForDeletion(); }
int nSlot = pBefore ? m_PageTabs.Find( pBefore ) : -1;
AddPage( page, title, NULL, showContext, nSlot ); SetActivePage( page ); } } }
bool PropertySheet::IsDroppable( CUtlVector< KeyValues * >& msglist ) { if ( !m_bDraggableTabs ) return false;
if ( msglist.Count() != 1 ) { return false; }
int mx, my; input()->GetCursorPos( mx, my ); ScreenToLocal( mx, my );
int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; if ( my > tabHeight ) return false;
PropertySheet *sheet = IsDroppingSheet( msglist ); if ( !sheet ) { return false; }
KeyValues *data = msglist[ 0 ]; char const *text = data->GetString( "tabname", "" ); Panel *page = reinterpret_cast< Panel * >( data->GetPtr( "propertypage" ) ); if ( !page ) return false;
AddPageDropTab( text, page );
return true; }
// Mouse is now over a droppable panel
void PropertySheet::OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ) { if ( m_nPageDropTabVisibleTime != -1 ) { // Extend timeout
m_nPageDropTabVisibleTime = system()->GetTimeMillis(); }
// Convert this panel's bounds to screen space
int x, y, w, h; GetSize( w, h );
int tabHeight = IsSmallTabs() ? m_iTabHeightSmall : m_iTabHeight; h = tabHeight + 4;
x = y = 0; LocalToScreen( x, y );
Color clr; clr = GetDropFrameColor();
surface()->DrawSetColor( clr ); // Draw 2 pixel frame
surface()->DrawOutlinedRect( x-1, y-1, x + w+1, y + h+1 );
clr[ 3 ] *= 0.5f; surface()->DrawSetColor( clr ); surface()->DrawOutlinedRect( x, y, x + w, y + h ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : state -
//-----------------------------------------------------------------------------
void PropertySheet::SetKBNavigationEnabled( bool state ) { m_bKBNavigationEnabled = state; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool PropertySheet::IsKBNavigationEnabled() const { return m_bKBNavigationEnabled; }
void PropertySheet::AddPageDropTab( char const *pTabName, Panel *pPage ) { //if ( m_nPageDropTabVisibleTime != -1 )
// return;
m_PageTabs.FindAndRemove( m_pDragDropTab );
m_pDragDropTab->SetText( pTabName ); m_pDragDropTab->Resize(); m_pDragDropTab->SetVisible( true );
int nPageSlot = FindPage( pPage ); if ( nPageSlot != -1 ) { m_pTemporarilyRemoved = m_PageTabs[ nPageSlot ]; m_pTemporarilyRemoved->SetVisible( false ); LayoutTabs(); }
PageTab *pBefore = FindInsertBeforeTab(); int nSlot = pBefore ? m_PageTabs.Find( pBefore ) : -1;
// Msg( "Add tab %s at slot %d\n", pTabName, nSlot );
if ( nSlot == -1 ) { m_PageTabs.AddToTail( m_pDragDropTab ); } else { m_PageTabs.InsertBefore( nSlot, m_pDragDropTab ); } LayoutTabs(); m_nPageDropTabVisibleTime = system()->GetTimeMillis(); }
void PropertySheet::ClearPageDropTab() { // Msg( "Clearing page drop tab\n" );
m_nPageDropTabVisibleTime = -1; m_PageTabs.FindAndRemove( m_pDragDropTab ); m_pDragDropTab->SetVisible( false ); if ( m_pTemporarilyRemoved ) { m_pTemporarilyRemoved->SetVisible( true ); m_pTemporarilyRemoved = NULL; } LayoutTabs(); }
void PropertySheet::OnThink() { BaseClass::OnThink(); if ( m_nPageDropTabVisibleTime == -1 ) return; if ( system()->GetTimeMillis() <= ( m_nPageDropTabVisibleTime + 100 ) ) return; ClearPageDropTab(); }
|