1. Allow the theme to create only the input handlers it customizes instead of forcing it to always create a handler even if the standard one is used: wxTheme::GetInputHandler() now takes wxInputConsumer to make this possible 2. Prefer delegation to inheritance when creating customized input handlers, almost all (except for wxStdScrollbarInputHandler) standard handler classes are now private, use wxClassName::GetStdInputHandler() to retrieve the standard handler for any class or polymorphic DoGetStdInputHandler() git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41227 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
1102 lines
28 KiB
C++
1102 lines
28 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/univ/slider.cpp
|
|
// Purpose: implementation of the universal version of wxSlider
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 09.02.01
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
There is some discrepancy in wxSL_LABELS style handling between wxMSW and
|
|
wxGTK: the latter handles it natively and shows only the current value of
|
|
the slider on the side corresponding to wxSL_TOP/BOTTOM/LEFT/RIGHT style
|
|
given (which can be combined with wxSL_HORIZONTAL/VERTICAL) while wxMSW
|
|
emulates this somehow and shows the min and max values near the ends of the
|
|
slider and the current value in a separate static box nearby.
|
|
|
|
We currently follow wxGTK except that wxSL_HORIZONTAL slider can only have
|
|
the label displayed on top or bottom of it and wxSL_VERTICAL - to the left
|
|
or right.
|
|
|
|
What we really need is probably a more fine grain control on labels, i.e. we
|
|
should be able to select if we show nothing at all, the current value only
|
|
or the value and the limits - the current approach is just that of the
|
|
lowest common denominator.
|
|
|
|
TODO:
|
|
|
|
+0. add ticks support
|
|
1. support for all orientations
|
|
2. draw the slider thumb highlighted when it is dragged
|
|
?3. manual ticks support?
|
|
*/
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_SLIDER
|
|
|
|
#include "wx/slider.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/dc.h"
|
|
#endif
|
|
|
|
#include "wx/univ/renderer.h"
|
|
#include "wx/univ/inphand.h"
|
|
#include "wx/univ/theme.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStdSliderInputHandler: default slider input handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class WXDLLEXPORT wxStdSliderInputHandler : public wxStdInputHandler
|
|
{
|
|
public:
|
|
// default ctor
|
|
wxStdSliderInputHandler(wxInputHandler *inphand)
|
|
: wxStdInputHandler(inphand)
|
|
{
|
|
}
|
|
|
|
// base class methods
|
|
virtual bool HandleKey(wxInputConsumer *consumer,
|
|
const wxKeyEvent& event,
|
|
bool pressed);
|
|
virtual bool HandleMouse(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event);
|
|
virtual bool HandleMouseMove(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event);
|
|
|
|
virtual bool HandleFocus(wxInputConsumer *consumer, const wxFocusEvent& event);
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// the margin between the slider and the label (FIXME: hardcoded)
|
|
static const wxCoord SLIDER_LABEL_MARGIN = 2;
|
|
|
|
// ============================================================================
|
|
// implementation of wxSlider
|
|
// ============================================================================
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
|
|
|
|
BEGIN_EVENT_TABLE(wxSlider, wxControl)
|
|
EVT_SIZE(wxSlider::OnSize)
|
|
END_EVENT_TABLE()
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider creation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#ifdef __VISUALC__
|
|
// warning C4355: 'this' : used in base member initializer list
|
|
#pragma warning(disable:4355)
|
|
#endif
|
|
|
|
wxSlider::wxSlider()
|
|
: m_thumb(this)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
wxSlider::wxSlider(wxWindow *parent,
|
|
wxWindowID id,
|
|
int value, int minValue, int maxValue,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
: m_thumb(this)
|
|
{
|
|
Init();
|
|
|
|
(void)Create(parent, id, value, minValue, maxValue,
|
|
pos, size, style, validator, name);
|
|
}
|
|
|
|
#ifdef __VISUALC__
|
|
// warning C4355: 'this' : used in base member initializer list
|
|
#pragma warning(default:4355)
|
|
#endif
|
|
|
|
void wxSlider::Init()
|
|
{
|
|
m_min =
|
|
m_max =
|
|
m_value = 0;
|
|
|
|
m_tickFreq = 1;
|
|
|
|
m_lineSize =
|
|
m_pageSize = 0;
|
|
|
|
m_thumbSize = 0;
|
|
m_thumbFlags = 0;
|
|
}
|
|
|
|
bool wxSlider::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
int value, int minValue, int maxValue,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
if ( !wxSliderBase::Create(parent, id, pos, size, style,
|
|
validator, name) )
|
|
return false;
|
|
|
|
SetRange(minValue, maxValue);
|
|
SetValue(value);
|
|
|
|
// call this after setting the range as the best size depends (at least if
|
|
// we have wxSL_LABELS style) on the range
|
|
SetBestSize(size);
|
|
|
|
CreateInputHandler(wxINP_HANDLER_SLIDER);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider range and value
|
|
// ----------------------------------------------------------------------------
|
|
|
|
int wxSlider::GetValue() const
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
int wxSlider::NormalizeValue(int value) const
|
|
{
|
|
if ( value < m_min )
|
|
return m_min;
|
|
else if ( value > m_max )
|
|
return m_max;
|
|
else
|
|
return value;
|
|
}
|
|
|
|
bool wxSlider::ChangeValueBy(int inc)
|
|
{
|
|
return ChangeValueTo(NormalizeValue(m_value + inc));
|
|
}
|
|
|
|
bool wxSlider::ChangeValueTo(int value)
|
|
{
|
|
// check if the value is going to change at all
|
|
if (value == m_value)
|
|
return false;
|
|
|
|
// this method is protected and we should only call it with normalized
|
|
// value!
|
|
wxCHECK_MSG( IsInRange(value), false, _T("invalid slider value") );
|
|
|
|
m_value = value;
|
|
|
|
Refresh();
|
|
|
|
// generate the events: both a specific scroll event and a command event
|
|
wxScrollEvent eventScroll(wxEVT_SCROLL_CHANGED, GetId());
|
|
eventScroll.SetPosition(m_value);
|
|
eventScroll.SetEventObject( this );
|
|
(void)GetEventHandler()->ProcessEvent(eventScroll);
|
|
|
|
wxCommandEvent event(wxEVT_COMMAND_SLIDER_UPDATED, GetId());
|
|
event.SetInt(m_value);
|
|
event.SetEventObject(this);
|
|
(void)GetEventHandler()->ProcessEvent(event);
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxSlider::SetValue(int value)
|
|
{
|
|
value = NormalizeValue(value);
|
|
|
|
if ( m_value != value )
|
|
{
|
|
m_value = value;
|
|
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
void wxSlider::SetRange(int minValue, int maxValue)
|
|
{
|
|
if ( minValue > maxValue )
|
|
{
|
|
// swap them, we always want min to be less than max
|
|
int tmp = minValue;
|
|
minValue = maxValue;
|
|
maxValue = tmp;
|
|
}
|
|
|
|
if ( m_min != minValue || m_max != maxValue )
|
|
{
|
|
m_min = minValue;
|
|
m_max = maxValue;
|
|
|
|
// reset the value to make sure it is in the new range
|
|
SetValue(m_value);
|
|
|
|
// the size of the label rect might have changed
|
|
if ( HasLabels() )
|
|
{
|
|
CalcGeometry();
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
//else: nothing changed
|
|
}
|
|
|
|
int wxSlider::GetMin() const
|
|
{
|
|
return m_min;
|
|
}
|
|
|
|
int wxSlider::GetMax() const
|
|
{
|
|
return m_max;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider line/page/thumb size
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxSlider::SetLineSize(int lineSize)
|
|
{
|
|
wxCHECK_RET( lineSize >= 0, _T("invalid slider line size") );
|
|
|
|
m_lineSize = lineSize;
|
|
}
|
|
|
|
void wxSlider::SetPageSize(int pageSize)
|
|
{
|
|
wxCHECK_RET( pageSize >= 0, _T("invalid slider page size") );
|
|
|
|
m_pageSize = pageSize;
|
|
}
|
|
|
|
int wxSlider::GetLineSize() const
|
|
{
|
|
if ( !m_lineSize )
|
|
{
|
|
// the default line increment is 1
|
|
wxConstCast(this, wxSlider)->m_lineSize = 1;
|
|
}
|
|
|
|
return m_lineSize;
|
|
}
|
|
|
|
int wxSlider::GetPageSize() const
|
|
{
|
|
if ( !m_pageSize )
|
|
{
|
|
// the default page increment is m_tickFreq
|
|
wxConstCast(this, wxSlider)->m_pageSize = m_tickFreq;
|
|
}
|
|
|
|
return m_pageSize;
|
|
}
|
|
|
|
void wxSlider::SetThumbLength(int lenPixels)
|
|
{
|
|
wxCHECK_RET( lenPixels >= 0, _T("invalid slider thumb size") );
|
|
|
|
// use m_thumbSize here directly and not GetThumbLength() to avoid setting
|
|
// it to the default value as we don't need it
|
|
if ( lenPixels != m_thumbSize )
|
|
{
|
|
m_thumbSize = lenPixels;
|
|
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
int wxSlider::GetThumbLength() const
|
|
{
|
|
wxSize sz = GetDefaultThumbSize();
|
|
int len = (IsVert() ? sz.x : sz.y);
|
|
if (m_thumbSize > len)
|
|
{
|
|
return m_thumbSize;
|
|
}
|
|
else
|
|
{
|
|
return len;
|
|
}
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider ticks
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxSlider::SetTickFreq(int n, int WXUNUSED(dummy))
|
|
{
|
|
wxCHECK_RET (n > 0, _T("invalid slider tick frequency"));
|
|
|
|
if ( n != m_tickFreq )
|
|
{
|
|
m_tickFreq = n;
|
|
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider geometry
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxSize wxSlider::CalcLabelSize() const
|
|
{
|
|
wxSize size;
|
|
|
|
// there is no sense in trying to calc the labels size if we haven't got
|
|
// any, the caller must check for it
|
|
wxCHECK_MSG( HasLabels(), size, _T("shouldn't be called") );
|
|
|
|
wxCoord w1, h1, w2, h2;
|
|
GetTextExtent(FormatValue(m_min), &w1, &h1);
|
|
GetTextExtent(FormatValue(m_max), &w2, &h2);
|
|
|
|
size.x = wxMax(w1, w2);
|
|
size.y = wxMax(h1, h2);
|
|
|
|
return size;
|
|
}
|
|
|
|
wxSize wxSlider::DoGetBestClientSize() const
|
|
{
|
|
// this dimension is completely arbitrary
|
|
static const wxCoord SLIDER_WIDTH = 40;
|
|
|
|
long style = GetWindowStyle();
|
|
|
|
// first calculate the size of the slider itself: i.e. the shaft and the
|
|
// thumb
|
|
wxCoord height = GetRenderer()->GetSliderDim();
|
|
|
|
wxSize size;
|
|
if ( IsVert() )
|
|
{
|
|
size.x = height;
|
|
size.y = SLIDER_WIDTH;
|
|
}
|
|
else // horizontal
|
|
{
|
|
size.x = SLIDER_WIDTH;
|
|
size.y = height;
|
|
}
|
|
|
|
// add space for ticks
|
|
if ( HasTicks() )
|
|
{
|
|
wxCoord lenTick = GetRenderer()->GetSliderTickLen();
|
|
if (style & wxSL_BOTH)
|
|
{
|
|
lenTick = 2 * lenTick;
|
|
}
|
|
|
|
if ( IsVert() )
|
|
size.x += lenTick;
|
|
else
|
|
size.y += lenTick;
|
|
}
|
|
|
|
// if we have the label, reserve enough space for it
|
|
if ( HasLabels() )
|
|
{
|
|
wxSize sizeLabels = CalcLabelSize();
|
|
|
|
if (style & (wxSL_LEFT|wxSL_RIGHT))
|
|
{
|
|
size.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
|
|
}
|
|
else if (style & (wxSL_TOP|wxSL_BOTTOM))
|
|
{
|
|
size.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void wxSlider::OnSize(wxSizeEvent& event)
|
|
{
|
|
CalcGeometry();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
const wxRect& wxSlider::GetSliderRect() const
|
|
{
|
|
if ( m_rectSlider.width < 0 )
|
|
{
|
|
wxConstCast(this, wxSlider)->CalcGeometry();
|
|
}
|
|
|
|
return m_rectSlider;
|
|
}
|
|
|
|
void wxSlider::CalcGeometry()
|
|
{
|
|
/*
|
|
recalc the label and slider positions, this looks like this for
|
|
wxSL_HORIZONTAL | wxSL_TOP slider:
|
|
|
|
LLL lll
|
|
-------------------------
|
|
| T | <-- this is the slider rect
|
|
| HHHHHHHHHHHHHHHTHHHHH |
|
|
| T |
|
|
| * * * * * * * *|
|
|
-------------------------
|
|
|
|
LLL is m_rectLabel as calculated here and lll is the real rect used for
|
|
label drawing in OnDraw() (TTT indicated the thumb position and *s are
|
|
the ticks)
|
|
|
|
in the wxSL_VERTICAL | wxSL_RIGHT case the picture is like this:
|
|
|
|
------ LLL
|
|
| H |
|
|
| H *|
|
|
| H |
|
|
| H *|
|
|
| H |
|
|
| H *|
|
|
| H |
|
|
|TTT*| lll
|
|
| H |
|
|
| H *|
|
|
------
|
|
*/
|
|
long style = GetWindowStyle();
|
|
|
|
// initialize to the full client rect
|
|
wxRect rectTotal = GetClientRect();
|
|
m_rectSlider = rectTotal;
|
|
wxSize sizeThumb = GetThumbSize();
|
|
|
|
// Labels reduce the size of the slider rect
|
|
if ( HasLabels() )
|
|
{
|
|
wxSize sizeLabels = CalcLabelSize();
|
|
|
|
m_rectLabel = wxRect(rectTotal.GetPosition(), sizeLabels);
|
|
|
|
if (style & wxSL_TOP)
|
|
{
|
|
// shrink and offset the slider to the bottom
|
|
m_rectSlider.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
|
|
m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
|
|
}
|
|
else if (style & wxSL_BOTTOM)
|
|
{
|
|
// shrink the slider and move the label to the bottom
|
|
m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
|
|
m_rectLabel.y += m_rectSlider.height + SLIDER_LABEL_MARGIN;
|
|
}
|
|
else if (style & wxSL_LEFT)
|
|
{
|
|
// shrink and offset the slider to the right
|
|
m_rectSlider.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
|
|
m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
|
|
}
|
|
else if (style & wxSL_RIGHT)
|
|
{
|
|
// shrink the slider and move the label to the right
|
|
m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
|
|
m_rectLabel.x += m_rectSlider.width + SLIDER_LABEL_MARGIN;
|
|
}
|
|
}
|
|
|
|
// calculate ticks too
|
|
if ( HasTicks() )
|
|
{
|
|
wxCoord lenTick = GetRenderer()->GetSliderTickLen();
|
|
|
|
// it
|
|
m_rectTicks = GetShaftRect();
|
|
|
|
if ( IsVert() )
|
|
{
|
|
if (style & (wxSL_LEFT|wxSL_BOTH))
|
|
{
|
|
m_rectTicks.x = m_rectSlider.x;
|
|
}
|
|
else
|
|
{ // wxSL_RIGHT
|
|
m_rectTicks.x = m_rectSlider.x + m_rectSlider.width - lenTick;
|
|
}
|
|
m_rectTicks.width = lenTick;
|
|
}
|
|
else // horizontal
|
|
{
|
|
if (style & (wxSL_TOP|wxSL_BOTH))
|
|
{
|
|
m_rectTicks.y = m_rectSlider.y;
|
|
}
|
|
else
|
|
{ // wxSL_BOTTOM
|
|
m_rectTicks.y = m_rectSlider.y + m_rectSlider.height - lenTick;
|
|
}
|
|
m_rectTicks.height = lenTick;
|
|
}
|
|
}
|
|
|
|
// slider is never smaller than thumb size unless rectTotal
|
|
if ( IsVert() )
|
|
{
|
|
wxCoord width = wxMin ( rectTotal.width, sizeThumb.x );
|
|
m_rectSlider.width = wxMax ( m_rectSlider.width, width );
|
|
}
|
|
else
|
|
{
|
|
wxCoord height = wxMin ( rectTotal.height, sizeThumb.y );
|
|
m_rectSlider.height = wxMax ( m_rectSlider.height, height );
|
|
}
|
|
}
|
|
|
|
wxSize wxSlider::GetDefaultThumbSize() const
|
|
{
|
|
// Default size has no styles (arrows)
|
|
return GetRenderer()->GetSliderThumbSize(GetSliderRect(), 0, GetOrientation());
|
|
}
|
|
|
|
wxSize wxSlider::GetThumbSize() const
|
|
{
|
|
return GetRenderer()->GetSliderThumbSize(GetSliderRect(), m_thumbSize, GetOrientation());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider thumb geometry
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxRect wxSlider::GetShaftRect() const
|
|
{
|
|
return GetRenderer()->GetSliderShaftRect(m_rectSlider, m_thumbSize, GetOrientation(), GetWindowStyle());
|
|
}
|
|
|
|
void wxSlider::CalcThumbRect(const wxRect *rectShaftIn,
|
|
wxRect *rectThumbOut,
|
|
wxRect *rectLabelOut,
|
|
int value) const
|
|
{
|
|
if ( value == INVALID_THUMB_VALUE )
|
|
{
|
|
// use the current if not specified
|
|
value = m_value;
|
|
}
|
|
|
|
bool isVertical = IsVert();
|
|
|
|
wxRect rectShaft;
|
|
if ( rectShaftIn )
|
|
{
|
|
rectShaft = *rectShaftIn;
|
|
}
|
|
else // no shaft rect provided, calc it
|
|
{
|
|
rectShaft = GetShaftRect();
|
|
}
|
|
|
|
wxCoord lenShaft,
|
|
lenThumb;
|
|
wxCoord *p;
|
|
|
|
wxRect rectThumb(rectShaft.GetPosition(), GetThumbSize());
|
|
if ( isVertical )
|
|
{
|
|
rectThumb.x += (rectShaft.width - rectThumb.width) / 2;
|
|
|
|
lenThumb = rectThumb.height;
|
|
lenShaft = rectShaft.height;
|
|
p = &rectThumb.y;
|
|
}
|
|
else // horz
|
|
{
|
|
rectThumb.y += (rectShaft.height - rectThumb.height) / 2;
|
|
|
|
lenThumb = rectThumb.width;
|
|
lenShaft = rectShaft.width;
|
|
p = &rectThumb.x;
|
|
}
|
|
|
|
// the thumb must always be entirely inside the shaft limits, so the max
|
|
// position is not at lenShaft but at lenShaft - thumbSize
|
|
if ( m_max != m_min )
|
|
{
|
|
if ( isVertical )
|
|
{
|
|
*p += ((lenShaft - lenThumb)*(m_max - value))/(m_max - m_min);
|
|
}
|
|
else
|
|
{ // horz
|
|
*p += ((lenShaft - lenThumb)*(value - m_min))/(m_max - m_min);
|
|
}
|
|
}
|
|
|
|
// calc the label rect
|
|
if ( HasLabels() && rectLabelOut )
|
|
{
|
|
long style = GetWindowStyle();
|
|
wxRect rectLabel = m_rectLabel;
|
|
|
|
// centre the label relatively to the thumb position
|
|
if (style & (wxSL_TOP|wxSL_BOTTOM))
|
|
{
|
|
rectLabel.x = rectThumb.x + (rectThumb.width - m_rectLabel.width)/2;
|
|
}
|
|
else if (style & (wxSL_LEFT|wxSL_RIGHT))
|
|
{
|
|
rectLabel.y = rectThumb.y + (rectThumb.height - m_rectLabel.height)/2;
|
|
}
|
|
|
|
*rectLabelOut = rectLabel;
|
|
}
|
|
|
|
if ( rectThumbOut )
|
|
|
|
*rectThumbOut = rectThumb;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider drawing
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxString wxSlider::FormatValue(int value) const
|
|
{
|
|
return wxString::Format(_T("%d"), value);
|
|
}
|
|
|
|
void wxSlider::DoDraw(wxControlRenderer *renderer)
|
|
{
|
|
wxRenderer *rend = GetRenderer();
|
|
wxDC& dc = renderer->GetDC();
|
|
wxRect rectUpdate = GetUpdateClientRect();
|
|
|
|
wxOrientation orient = GetOrientation();
|
|
int flags = GetStateFlags();
|
|
long style = GetWindowStyle();
|
|
|
|
wxSize sz = GetThumbSize();
|
|
int len = IsVert() ? sz.x : sz.y;
|
|
|
|
// first draw the shaft
|
|
wxRect rectShaft = rend->GetSliderShaftRect(m_rectSlider, len, orient, style);
|
|
if ( rectUpdate.Intersects(rectShaft) )
|
|
{
|
|
rend->DrawSliderShaft(dc, m_rectSlider, len, orient, flags, style);
|
|
}
|
|
|
|
// calculate the thumb position in pixels and draw it
|
|
wxRect rectThumb, rectLabel;
|
|
CalcThumbRect(&rectShaft, &rectThumb, &rectLabel);
|
|
|
|
// then draw the ticks
|
|
if ( HasTicks() && rectUpdate.Intersects(m_rectTicks) )
|
|
{
|
|
rend->DrawSliderTicks(dc, m_rectSlider, len, orient,
|
|
m_min, m_max, m_tickFreq, flags, style);
|
|
}
|
|
|
|
// then draw the thumb
|
|
if ( rectUpdate.Intersects(rectThumb) )
|
|
{
|
|
rend->DrawSliderThumb(dc, rectThumb, orient, flags | m_thumbFlags, style);
|
|
}
|
|
|
|
// finally, draw the label near the thumb
|
|
if ( HasLabels() && rectUpdate.Intersects(rectLabel) )
|
|
{
|
|
// align it to be close to the shaft
|
|
int align = 0;
|
|
if (style & wxSL_TOP)
|
|
{
|
|
align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_TOP;
|
|
}
|
|
else if (style & wxSL_BOTTOM)
|
|
{
|
|
align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_BOTTOM;
|
|
}
|
|
else if (style & wxSL_LEFT)
|
|
{
|
|
align = wxALIGN_CENTRE_VERTICAL|wxALIGN_LEFT;
|
|
}
|
|
else if (style & wxSL_RIGHT)
|
|
{
|
|
align = wxALIGN_CENTRE_VERTICAL|wxALIGN_RIGHT;
|
|
}
|
|
|
|
dc.SetFont(GetFont());
|
|
dc.SetTextForeground(GetForegroundColour());
|
|
|
|
// the slider label is never drawn focused
|
|
rend->DrawLabel(dc, FormatValue(m_value), rectLabel,
|
|
flags & ~wxCONTROL_FOCUSED, align);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider input processing
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxSlider::PerformAction(const wxControlAction& action,
|
|
long numArg,
|
|
const wxString& strArg)
|
|
{
|
|
wxEventType scrollEvent = wxEVT_NULL;
|
|
int value;
|
|
bool valueChanged = true;
|
|
|
|
if ( action == wxACTION_SLIDER_START )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_TOP;
|
|
value = m_min;
|
|
}
|
|
else if ( action == wxACTION_SLIDER_END )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_BOTTOM;
|
|
value = m_max;
|
|
}
|
|
else if ( action == wxACTION_SLIDER_PAGE_CHANGE )
|
|
{
|
|
value = NormalizeValue(m_value + numArg * GetPageSize());
|
|
}
|
|
else if ( action == wxACTION_SLIDER_LINE_UP )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_LINEUP;
|
|
value = NormalizeValue(m_value + +GetLineSize());
|
|
}
|
|
else if ( action == wxACTION_SLIDER_LINE_DOWN )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_LINEDOWN;
|
|
value = NormalizeValue(m_value + -GetLineSize());
|
|
}
|
|
else if ( action == wxACTION_SLIDER_PAGE_UP )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_PAGEUP;
|
|
value = NormalizeValue(m_value + +GetPageSize());
|
|
}
|
|
else if ( action == wxACTION_SLIDER_PAGE_DOWN )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_PAGEDOWN;
|
|
value = NormalizeValue(m_value + -GetPageSize());
|
|
}
|
|
else if ( action == wxACTION_SLIDER_THUMB_DRAG ||
|
|
action == wxACTION_SLIDER_THUMB_MOVE )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_THUMBTRACK;
|
|
|
|
// we shouldn't generate a command event about this change but we still
|
|
// should update our value and the slider appearance
|
|
valueChanged = false;
|
|
m_value =
|
|
value = (int)numArg;
|
|
Refresh();
|
|
}
|
|
else if ( action == wxACTION_SLIDER_THUMB_RELEASE )
|
|
{
|
|
scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
|
|
value = (int)numArg;
|
|
}
|
|
else
|
|
{
|
|
return wxControl::PerformAction(action, numArg, strArg);
|
|
}
|
|
|
|
// update wxSlider current value and generate wxCommandEvent, except while
|
|
// dragging the thumb
|
|
if ( valueChanged )
|
|
ChangeValueTo(value);
|
|
|
|
// also generate more precise wxScrollEvent if applicable
|
|
if ( scrollEvent != wxEVT_NULL )
|
|
{
|
|
wxScrollEvent event(scrollEvent, GetId());
|
|
event.SetPosition(value);
|
|
event.SetEventObject( this );
|
|
GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
wxInputHandler *wxSlider::GetStdInputHandler(wxInputHandler *handlerDef)
|
|
{
|
|
static wxStdSliderInputHandler s_handler(handlerDef);
|
|
|
|
return &s_handler;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxSlider implementation of wxControlWithThumb interface
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxScrollThumb::Shaft wxSlider::HitTest(const wxPoint& pt) const
|
|
{
|
|
wxRect rectShaft = GetShaftRect();
|
|
wxRect rectThumb;
|
|
CalcThumbRect(&rectShaft, &rectThumb, NULL);
|
|
|
|
// check for possible shaft or thumb hit
|
|
if (!rectShaft.Contains(pt) && !rectThumb.Contains(pt))
|
|
{
|
|
return wxScrollThumb::Shaft_None;
|
|
}
|
|
|
|
// the position to test and the start and end of the thumb
|
|
wxCoord x, x1, x2, x3, x4;
|
|
if (IsVert())
|
|
{
|
|
x = pt.y;
|
|
x1 = rectThumb.GetBottom();
|
|
x2 = rectShaft.GetBottom();
|
|
x3 = rectShaft.GetTop();
|
|
x4 = rectThumb.GetTop();
|
|
}
|
|
else
|
|
{ // horz
|
|
x = pt.x;
|
|
x1 = rectShaft.GetLeft();
|
|
x2 = rectThumb.GetLeft();
|
|
x3 = rectThumb.GetRight();
|
|
x4 = rectShaft.GetRight();
|
|
}
|
|
if ((x1 <= x) && (x < x2))
|
|
{
|
|
// or to the left
|
|
return wxScrollThumb::Shaft_Above;
|
|
}
|
|
|
|
if ((x3 < x) && (x <= x4)) {
|
|
// or to the right
|
|
return wxScrollThumb::Shaft_Below;
|
|
}
|
|
|
|
// where else can it be?
|
|
return wxScrollThumb::Shaft_Thumb;
|
|
}
|
|
|
|
wxCoord wxSlider::ThumbPosToPixel() const
|
|
{
|
|
wxRect rectThumb;
|
|
CalcThumbRect(NULL, &rectThumb, NULL);
|
|
|
|
return IsVert() ? rectThumb.y : rectThumb.x;
|
|
}
|
|
|
|
int wxSlider::PixelToThumbPos(wxCoord x) const
|
|
{
|
|
wxRect rectShaft = GetShaftRect();
|
|
wxSize sizeThumb = GetThumbSize();
|
|
|
|
wxCoord x0, len;
|
|
if ( IsVert() )
|
|
{
|
|
x0 = rectShaft.y;
|
|
len = rectShaft.height - sizeThumb.y;
|
|
}
|
|
else // horz
|
|
{
|
|
x0 = rectShaft.x;
|
|
len = rectShaft.width - sizeThumb.x;
|
|
}
|
|
|
|
int pos = m_min;
|
|
if ( len > 0 )
|
|
{
|
|
if ( x > x0 )
|
|
{
|
|
pos += ((x - x0) * (m_max - m_min)) / len;
|
|
if ( pos > m_max )
|
|
pos = m_max;
|
|
}
|
|
//else: x <= x0, leave pos = min
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart,
|
|
int flag,
|
|
bool set)
|
|
{
|
|
// for now we ignore the flags for the shaft as no renderer uses them
|
|
// anyhow
|
|
if ( shaftPart == wxScrollThumb::Shaft_Thumb )
|
|
{
|
|
if ( set )
|
|
m_thumbFlags |= flag;
|
|
else
|
|
m_thumbFlags &= ~flag;
|
|
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
void wxSlider::OnThumbDragStart(int pos)
|
|
{
|
|
if (IsVert())
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_DRAG, m_max - pos);
|
|
}
|
|
else
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_DRAG, pos);
|
|
}
|
|
}
|
|
|
|
void wxSlider::OnThumbDrag(int pos)
|
|
{
|
|
if (IsVert())
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_MOVE, m_max - pos);
|
|
}
|
|
else
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_MOVE, pos);
|
|
}
|
|
}
|
|
|
|
void wxSlider::OnThumbDragEnd(int pos)
|
|
{
|
|
if (IsVert())
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_RELEASE, m_max - pos);
|
|
}
|
|
else
|
|
{
|
|
PerformAction(wxACTION_SLIDER_THUMB_RELEASE, pos);
|
|
}
|
|
}
|
|
|
|
void wxSlider::OnPageScrollStart()
|
|
{
|
|
// we do nothing here
|
|
}
|
|
|
|
bool wxSlider::OnPageScroll(int pageInc)
|
|
{
|
|
int value = GetValue();
|
|
PerformAction(wxACTION_SLIDER_PAGE_CHANGE, pageInc);
|
|
|
|
return GetValue() != value;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStdSliderInputHandler
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxStdSliderInputHandler::HandleKey(wxInputConsumer *consumer,
|
|
const wxKeyEvent& event,
|
|
bool pressed)
|
|
{
|
|
if ( pressed )
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
|
|
wxControlAction action;
|
|
switch ( keycode )
|
|
{
|
|
case WXK_HOME:
|
|
action = wxACTION_SLIDER_END;
|
|
break;
|
|
|
|
case WXK_END:
|
|
action = wxACTION_SLIDER_START;
|
|
break;
|
|
|
|
case WXK_RIGHT:
|
|
case WXK_UP:
|
|
action = wxACTION_SLIDER_LINE_UP;
|
|
break;
|
|
|
|
case WXK_LEFT:
|
|
case WXK_DOWN:
|
|
action = wxACTION_SLIDER_LINE_DOWN;
|
|
break;
|
|
|
|
case WXK_PAGEUP:
|
|
action = wxACTION_SLIDER_PAGE_UP;
|
|
break;
|
|
|
|
case WXK_PAGEDOWN:
|
|
action = wxACTION_SLIDER_PAGE_DOWN;
|
|
break;
|
|
}
|
|
|
|
if ( !action.IsEmpty() )
|
|
{
|
|
consumer->PerformAction(action);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return wxStdInputHandler::HandleKey(consumer, event, pressed);
|
|
}
|
|
|
|
bool wxStdSliderInputHandler::HandleMouse(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event)
|
|
{
|
|
wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
|
|
|
|
if ( slider->GetThumb().HandleMouse(event) )
|
|
{
|
|
// processed by the thumb
|
|
return false;
|
|
}
|
|
|
|
return wxStdInputHandler::HandleMouse(consumer, event);
|
|
}
|
|
|
|
bool wxStdSliderInputHandler::HandleMouseMove(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event)
|
|
{
|
|
wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
|
|
|
|
if ( slider->GetThumb().HandleMouseMove(event) )
|
|
{
|
|
// processed by the thumb
|
|
return false;
|
|
}
|
|
|
|
return wxStdInputHandler::HandleMouseMove(consumer, event);
|
|
}
|
|
|
|
bool
|
|
wxStdSliderInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
|
|
const wxFocusEvent& WXUNUSED(event))
|
|
{
|
|
// slider appearance changes when it gets/loses focus
|
|
return true;
|
|
}
|
|
|
|
#endif // wxUSE_SLIDER
|