source-engine/vgui2/vgui_controls/ScrollBar.cpp

805 lines
22 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <assert.h>
#include <vgui/IScheme.h>
#include <vgui/ISystem.h>
#include <vgui/IInput.h>
#include <vgui/IImage.h>
#include <KeyValues.h>
#include <vgui_controls/ScrollBar.h>
#include <vgui_controls/ScrollBarSlider.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/ImagePanel.h>
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
using namespace vgui;
namespace
{
enum
{ // scroll bar will scroll a little, then continuous scroll like in windows
SCROLL_BAR_DELAY = 400, // default delay for all scroll bars
SCROLL_BAR_SPEED = 50, // this is how fast the bar scrolls when you hold down the arrow button
SCROLLBAR_DEFAULT_WIDTH = 17,
};
//-----------------------------------------------------------------------------
// Purpose: Scroll bar button-the arrow button that moves the slider up and down.
//-----------------------------------------------------------------------------
class ScrollBarButton : public Button
{
public:
ScrollBarButton(Panel *parent, const char *panelName, const char *text) : Button(parent, panelName, text)
{
SetButtonActivationType(ACTIVATE_ONPRESSED);
SetContentAlignment(Label::a_center);
}
void OnMouseFocusTicked()
{
// pass straight up to parent
CallParentFunction(new KeyValues("MouseFocusTicked"));
}
virtual void ApplySchemeSettings(IScheme *pScheme)
{
Button::ApplySchemeSettings(pScheme);
SetFont(pScheme->GetFont("Marlett", IsProportional() ));
SetDefaultBorder(pScheme->GetBorder("ScrollBarButtonBorder"));
SetDepressedBorder(pScheme->GetBorder("ScrollBarButtonDepressedBorder"));
SetDefaultColor(GetSchemeColor("ScrollBarButton.FgColor", pScheme), GetSchemeColor("ScrollBarButton.BgColor", pScheme));
SetArmedColor(GetSchemeColor("ScrollBarButton.ArmedFgColor", pScheme), GetSchemeColor("ScrollBarButton.ArmedBgColor", pScheme));
SetDepressedColor(GetSchemeColor("ScrollBarButton.DepressedFgColor", pScheme), GetSchemeColor("ScrollBarButton.DepressedBgColor", pScheme));
}
// Don't request focus.
// This will keep cursor focus in main window in text entry windows.
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());
}
}
virtual void OnMouseReleased(MouseCode code)
{
if (!IsEnabled())
return;
if (!IsMouseClickEnabled(code))
return;
if (IsUseCaptureMouseEnabled())
{
{
SetSelected(false);
Repaint();
}
// lock mouse input to going to this button
input()->SetMouseCapture(NULL);
}
if( input()->GetMouseOver() == GetVPanel() )
{
SetArmed( true );
}
}
};
}
vgui::Panel *ScrollBar_Vertical_Factory()
{
return new ScrollBar(NULL, NULL, true );
}
vgui::Panel *ScrollBar_Horizontal_Factory()
{
return new ScrollBar(NULL, NULL, false );
}
DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Vertical, ScrollBar_Vertical_Factory );
DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( ScrollBar, ScrollBar_Horizontal, ScrollBar_Horizontal_Factory );
// Default is a horizontal one
DECLARE_BUILD_FACTORY_CUSTOM( ScrollBar, ScrollBar_Horizontal_Factory );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
ScrollBar::ScrollBar(Panel *parent, const char *panelName, bool vertical) : Panel(parent, panelName)
{
_slider=null;
_button[0]=null;
_button[1]=null;
_scrollDelay = SCROLL_BAR_DELAY;
_respond = true;
m_pUpArrow = NULL;
m_pLine = NULL;
m_pDownArrow = NULL;
m_pBox = NULL;
m_bNoButtons = false;
m_pOverriddenButtons[0] = NULL;
m_pOverriddenButtons[1] = NULL;
2022-04-16 09:20:36 +00:00
int width = IsProportional() ? scheme()->GetProportionalScaledValue(SCROLLBAR_DEFAULT_WIDTH) : SCROLLBAR_DEFAULT_WIDTH;
2020-04-22 16:56:21 +00:00
if (vertical)
{
// FIXME: proportional changes needed???
SetSlider(new ScrollBarSlider(NULL, "Slider", true));
SetButton(new ScrollBarButton(NULL, "UpButton", "t"), 0);
SetButton(new ScrollBarButton(NULL, "DownButton", "u"), 1);
_button[0]->SetTextInset(0, 1);
_button[1]->SetTextInset(0, -1);
2022-04-16 09:20:36 +00:00
SetSize(width, 64);
2020-04-22 16:56:21 +00:00
}
else
{
SetSlider(new ScrollBarSlider(NULL, NULL, false));
SetButton(new ScrollBarButton(NULL, NULL, "w"), 0);
SetButton(new ScrollBarButton(NULL, NULL, "4"), 1);
_button[0]->SetTextInset(0, 0);
_button[1]->SetTextInset(0, 0);
2022-04-16 09:20:36 +00:00
SetSize(64, width);
2020-04-22 16:56:21 +00:00
}
Panel::SetPaintBorderEnabled(true);
Panel::SetPaintBackgroundEnabled(false);
Panel::SetPaintEnabled(true);
SetButtonPressedScrollValue(20);
SetBlockDragChaining( true );
Validate();
}
//-----------------------------------------------------------------------------
// Purpose: sets up the width of the scrollbar according to the scheme
//-----------------------------------------------------------------------------
void ScrollBar::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
const char *resourceString = pScheme->GetResourceString("ScrollBar.Wide");
if (resourceString)
{
int value = atoi(resourceString);
if (IsProportional())
{
value = scheme()->GetProportionalScaledValueEx(GetScheme(), value);
}
if (_slider && _slider->IsVertical())
{
// we're vertical, so reset the width
SetSize( value, GetTall() );
}
else
{
// we're horizontal, so the width means the height
SetSize( GetWide(), value );
}
}
UpdateButtonsForImages();
}
//-----------------------------------------------------------------------------
// Purpose: Set the slider's Paint border enabled.
//-----------------------------------------------------------------------------
void ScrollBar::SetPaintBorderEnabled(bool state)
{
if ( _slider )
{
_slider->SetPaintBorderEnabled( state );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::SetPaintBackgroundEnabled(bool state)
{
if ( _slider )
{
_slider->SetPaintBackgroundEnabled( state );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::SetPaintEnabled(bool state)
{
if ( _slider )
{
_slider->SetPaintEnabled( state );
}
}
//-----------------------------------------------------------------------------
// Purpose: Layout the scroll bar and buttons on screen
//-----------------------------------------------------------------------------
void ScrollBar::PerformLayout()
{
if (_slider)
{
int wide, tall;
GetPaintSize(wide,tall);
if(_slider->IsVertical())
{
if ( m_bNoButtons )
{
_slider->SetBounds(0, 0, wide, tall + 1);
}
else
{
_slider->SetBounds(0, wide, wide, tall-(wide*2)+1);
_button[0]->SetBounds(0,0, wide, wide );
_button[1]->SetBounds(0,tall-wide ,wide, wide );
}
}
else
{
if ( m_bNoButtons )
{
_slider->SetBounds(tall, 0, wide, tall + 1);
}
else
{
_slider->SetBounds(tall, -1, wide-(tall*2)+1, tall + 1 );
_button[0]->SetBounds(0, 0, tall, tall);
_button[1]->SetBounds(wide-tall, 0, tall, tall);
}
}
// Place the images over the appropriate controls
int x,y;
if ( m_pUpArrow )
{
_button[0]->GetBounds( x,y,wide,tall );
m_pUpArrow->SetBounds( x,y,wide,tall );
}
if ( m_pDownArrow )
{
_button[1]->GetBounds( x,y,wide,tall );
m_pDownArrow->SetBounds( x,y,wide,tall );
}
if ( m_pLine )
{
_slider->GetBounds( x,y,wide,tall );
m_pLine->SetBounds( x,y,wide,tall );
}
if ( m_pBox )
{
m_pBox->SetBounds( 0, wide, wide, wide );
}
_slider->MoveToFront();
// after resizing our child, we should remind it to perform a layout
_slider->InvalidateLayout();
UpdateSliderImages();
}
if ( m_bAutoHideButtons )
{
SetScrollbarButtonsVisible( _slider->IsSliderVisible() );
}
// get tooltips to draw
Panel::PerformLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Set the value of the scroll bar slider.
//-----------------------------------------------------------------------------
void ScrollBar::SetValue(int value)
{
_slider->SetValue(value);
}
//-----------------------------------------------------------------------------
// Purpose: Get the value of the scroll bar slider.
//-----------------------------------------------------------------------------
int ScrollBar::GetValue()
{
return _slider->GetValue();
}
//-----------------------------------------------------------------------------
// Purpose: Set the range of the scroll bar slider.
// This the range of numbers the slider can scroll through.
//-----------------------------------------------------------------------------
void ScrollBar::SetRange(int min,int max)
{
_slider->SetRange(min,max);
}
//-----------------------------------------------------------------------------
// Purpose: Gets the range of the scroll bar slider.
// This the range of numbers the slider can scroll through.
//-----------------------------------------------------------------------------
void ScrollBar::GetRange(int &min, int &max)
{
_slider->GetRange(min, max);
}
//-----------------------------------------------------------------------------
// Purpose: Send a message when the slider is moved.
// Input : value -
//-----------------------------------------------------------------------------
void ScrollBar::SendSliderMoveMessage(int value)
{
PostActionSignal(new KeyValues("ScrollBarSliderMoved", "position", value));
}
//-----------------------------------------------------------------------------
// Purpose: Called when the Slider is dragged by the user
// Input : value -
//-----------------------------------------------------------------------------
void ScrollBar::OnSliderMoved(int value)
{
SendSliderMoveMessage(value);
UpdateSliderImages();
}
//-----------------------------------------------------------------------------
// Purpose: Check if the scrollbar is vertical (true) or horizontal (false)
//-----------------------------------------------------------------------------
bool ScrollBar::IsVertical()
{
return _slider->IsVertical();
}
//-----------------------------------------------------------------------------
// Purpose: Check if the the scrollbar slider has full range.
// Normally if you have a scroll bar and the range goes from a to b and
// the slider is sized to c, the range will go from a to b-c.
// This makes it so the slider goes from a to b fully.
//-----------------------------------------------------------------------------
bool ScrollBar::HasFullRange()
{
return _slider->HasFullRange();
}
//-----------------------------------------------------------------------------
// Purpose: Setup the indexed scroll bar button with the input params.
//-----------------------------------------------------------------------------
//LEAK: new and old slider will leak
void ScrollBar::SetButton(Button *button, int index)
{
if(_button[index]!=null)
{
_button[index]->SetParent((Panel *)NULL);
}
_button[index]=button;
_button[index]->SetParent(this);
_button[index]->AddActionSignalTarget(this);
_button[index]->SetCommand(new KeyValues("ScrollButtonPressed", "index", index));
Validate();
}
//-----------------------------------------------------------------------------
// Purpose: Return the indexed scroll bar button
//-----------------------------------------------------------------------------
Button* ScrollBar::GetButton(int index)
{
return _button[index];
}
//-----------------------------------------------------------------------------
// Purpose: Set up the slider.
//-----------------------------------------------------------------------------
//LEAK: new and old slider will leak
void ScrollBar::SetSlider(ScrollBarSlider *slider)
{
if(_slider!=null)
{
_slider->SetParent((Panel *)NULL);
}
_slider=slider;
_slider->AddActionSignalTarget(this);
_slider->SetParent(this);
Validate();
}
//-----------------------------------------------------------------------------
// Purpose: Return a pointer to the slider.
//-----------------------------------------------------------------------------
ScrollBarSlider *ScrollBar::GetSlider()
{
return _slider;
}
Button *ScrollBar::GetDepressedButton( int iIndex )
{
if ( iIndex == 0 )
return ( m_pOverriddenButtons[0] ? m_pOverriddenButtons[0] : _button[0] );
return ( m_pOverriddenButtons[1] ? m_pOverriddenButtons[1] : _button[1] );
}
//-----------------------------------------------------------------------------
// Purpose: Scrolls in response to clicking and holding on up or down arrow
// The idea is to have the slider move one step then delay a bit and then
// the bar starts moving at normal speed. This gives a stepping feeling
// to just clicking an arrow once.
//-----------------------------------------------------------------------------
void ScrollBar::OnMouseFocusTicked()
{
int direction = 0;
// top button is down
if ( GetDepressedButton(0)->IsDepressed() )
{
direction = -1;
}
// bottom top button is down
else if (GetDepressedButton(1)->IsDepressed())
{
direction = 1;
}
// a button is down
if ( direction != 0 )
{
RespondToScrollArrow(direction);
if (_scrollDelay < system()->GetTimeMillis())
{
_scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_SPEED;
_respond = true;
}
else
{
_respond = false;
}
}
// a button is not down.
else
{
// if neither button is down keep delay at max
_scrollDelay = system()->GetTimeMillis() + SCROLL_BAR_DELAY;
_respond = true;
}
}
//-----------------------------------------------------------------------------
// Purpose: move scroll bar in response to the first button
// Input: button and direction to move scroll bar when that button is pressed
// direction can only by +/- 1
// Output: whether button is down or not
//-----------------------------------------------------------------------------
void ScrollBar::RespondToScrollArrow(int const direction)
{
if (_respond)
{
int newValue = _slider->GetValue() + (direction * _buttonPressedScrollValue);
_slider->SetValue(newValue);
SendSliderMoveMessage(newValue);
}
}
//-----------------------------------------------------------------------------
// Purpose: Trigger layout changes when the window size is changed.
//-----------------------------------------------------------------------------
void ScrollBar::OnSizeChanged(int wide, int tall)
{
InvalidateLayout();
_slider->InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Set how far the scroll bar slider moves when a scroll bar button is
// pressed.
//-----------------------------------------------------------------------------
void ScrollBar::SetButtonPressedScrollValue(int value)
{
_buttonPressedScrollValue=value;
}
//-----------------------------------------------------------------------------
// Purpose: Set the range of the rangewindow. This is how many
// lines are displayed at one time
// in the window the scroll bar is attached to.
// This also controls the size of the slider, its size is proportional
// to the number of lines displayed / total number of lines.
//-----------------------------------------------------------------------------
void ScrollBar::SetRangeWindow(int rangeWindow)
{
_slider->SetRangeWindow(rangeWindow);
}
//-----------------------------------------------------------------------------
// Purpose: Get the range of the rangewindow. This is how many
// lines are displayed at one time
// in the window the scroll bar is attached to.
// This also controls the size of the slider, its size is proportional
// to the number of lines displayed / total number of lines.
//-----------------------------------------------------------------------------
int ScrollBar::GetRangeWindow()
{
return _slider->GetRangeWindow();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::Validate()
{
if ( _slider != null )
{
int buttonOffset = 0;
for( int i=0; i<2; i++ )
{
if( _button[i] != null )
{
if( _button[i]->IsVisible() )
{
if( _slider->IsVertical() )
{
buttonOffset += _button[i]->GetTall();
}
else
{
buttonOffset += _button[i]->GetWide();
}
}
}
}
_slider->SetButtonOffset(buttonOffset);
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::SetScrollbarButtonsVisible(bool visible)
{
for( int i=0; i<2; i++ )
{
if( _button[i] != null )
{
_button[i]->SetShouldPaint( visible );
_button[i]->SetEnabled( visible );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::UseImages( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox )
{
if ( pszUpArrow )
{
if ( !m_pUpArrow )
{
m_pUpArrow = new vgui::ImagePanel( this, "UpArrow" );
if ( m_pUpArrow )
{
m_pUpArrow->SetImage( pszUpArrow );
m_pUpArrow->SetShouldScaleImage( true );
m_pUpArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
m_pUpArrow->SetAlpha( 255 );
m_pUpArrow->SetZPos( -1 );
}
}
m_pUpArrow->SetImage( pszUpArrow );
m_pUpArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
}
else if ( m_pUpArrow )
{
m_pUpArrow->MarkForDeletion();
m_pUpArrow = NULL;
}
if ( pszDownArrow )
{
if ( !m_pDownArrow )
{
m_pDownArrow = new vgui::ImagePanel( this, "DownArrow" );
if ( m_pDownArrow )
{
m_pDownArrow->SetShouldScaleImage( true );
m_pDownArrow->SetFgColor( Color( 255, 255, 255, 255 ) );
m_pDownArrow->SetAlpha( 255 );
m_pDownArrow->SetZPos( -1 );
}
}
m_pDownArrow->SetImage( pszDownArrow );
m_pDownArrow->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
}
else if ( m_pDownArrow )
{
m_pDownArrow->MarkForDeletion();
m_pDownArrow = NULL;
}
if ( pszLine )
{
if ( !m_pLine )
{
m_pLine = new ImagePanel( this, "Line" );
if ( m_pLine )
{
m_pLine->SetShouldScaleImage( true );
m_pLine->SetZPos( -1 );
}
}
m_pLine->SetImage( pszLine );
m_pLine->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
}
else if ( m_pLine )
{
m_pLine->MarkForDeletion();
m_pLine = NULL;
}
if ( pszBox )
{
if ( !m_pBox )
{
m_pBox = new ImagePanel( this, "Box" );
if ( m_pBox )
{
m_pBox->SetShouldScaleImage( true );
m_pBox->SetZPos( -1 );
}
}
m_pBox->SetImage( pszBox );
m_pBox->SetRotation( IsVertical() ? ROTATED_UNROTATED : ROTATED_CLOCKWISE_90 );
}
else if ( m_pBox )
{
m_pBox->MarkForDeletion();
m_pBox = NULL;
}
UpdateButtonsForImages();
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::UpdateButtonsForImages( void )
{
// Turn off parts of our drawing based on which images we're replacing it with
if ( m_pUpArrow || m_pDownArrow )
{
SetScrollbarButtonsVisible( false );
_button[0]->SetPaintBorderEnabled( false );
_button[1]->SetPaintBorderEnabled( false );
m_bAutoHideButtons = false;
}
if ( m_pLine || m_pBox )
{
SetPaintBackgroundEnabled( false );
SetPaintBorderEnabled( false );
if ( _slider )
{
_slider->SetPaintEnabled( false );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ScrollBar::UpdateSliderImages( void )
{
if ( m_pUpArrow && m_pDownArrow )
{
// set the alpha on the up arrow
int nMin, nMax;
GetRange( nMin, nMax );
int nScrollPos = GetValue();
int nRangeWindow = GetRangeWindow();
int nBottom = nMax - nRangeWindow;
if ( nBottom < 0 )
{
nBottom = 0;
}
// set the alpha on the up arrow
int nAlpha = ( nScrollPos - nMin <= 0 ) ? 90 : 255;
m_pUpArrow->SetAlpha( nAlpha );
// set the alpha on the down arrow
nAlpha = ( nScrollPos >= nBottom ) ? 90 : 255;
m_pDownArrow->SetAlpha( nAlpha );
}
if ( m_pLine && m_pBox )
{
ScrollBarSlider *pSlider = GetSlider();
if ( pSlider && pSlider->GetRangeWindow() > 0 )
{
int x, y, w, t, min, max;
m_pLine->GetBounds( x, y, w, t );
// If our slider needs layout, force it to do it now
if ( pSlider->IsLayoutInvalid() )
{
pSlider->InvalidateLayout( true );
}
pSlider->GetNobPos( min, max );
if ( IsVertical() )
{
m_pBox->SetBounds( x, y + min, w, ( max - min ) );
}
else
{
m_pBox->SetBounds( x + min, 0, (max-min), t );
}
}
}
}
void ScrollBar::ApplySettings( KeyValues *pInResourceData )
{
BaseClass::ApplySettings( pInResourceData );
m_bNoButtons = pInResourceData->GetBool( "nobuttons", false );
KeyValues *pSliderKV = pInResourceData->FindKey( "Slider" );
if ( pSliderKV && _slider )
{
_slider->ApplySettings( pSliderKV );
}
KeyValues *pDownButtonKV = pInResourceData->FindKey( "DownButton" );
if ( pDownButtonKV && _button[0] )
{
_button[0]->ApplySettings( pDownButtonKV );
}
KeyValues *pUpButtonKV = pInResourceData->FindKey( "UpButton" );
if ( pUpButtonKV && _button[0] )
{
_button[1]->ApplySettings( pUpButtonKV );
}
}