1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        univ/slider.cpp 
   3 // Purpose:     implementation of the universal version of wxSlider 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 SciTech Software, Inc. (www.scitechsoft.com) 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  13    There is some discrepancy in wxSL_LABELS style handling between wxMSW and 
  14    wxGTK: the latter handles it natively and shows only the current value of 
  15    the slider on the side corresponding to wxSL_TOP/BOTTOM/LEFT/RIGHT style 
  16    given (which can be combined with wxSL_HORIZONTAL/VERTICAL) while wxMSW 
  17    emulates this somehow and shows the min and max values near the ends of the 
  18    slider and the current value in a separate static box nearby. 
  20    We currently follow wxGTK except that wxSL_HORIZONTAL slider can only have 
  21    the label displayed on top or bottom of it and wxSL_VERTICAL - to the left 
  24    What we really need is probably a more fine grain control on labels, i.e. we 
  25    should be able to select if we show nothign at all, the current value only 
  26    or the value and the limits - the current approach is just that of the 
  27    greatest common denominator. 
  32    1. support for all orientations 
  33    2. draw the slider thumb highlighted when it is dragged 
  34   ?3. manual ticks support? 
  37 // ============================================================================ 
  39 // ============================================================================ 
  41 // ---------------------------------------------------------------------------- 
  43 // ---------------------------------------------------------------------------- 
  46     #pragma implementation "univslider.h" 
  49 #include "wx/wxprec.h" 
  59 #include "wx/slider.h" 
  63 #include "wx/univ/renderer.h" 
  64 #include "wx/univ/inphand.h" 
  65 #include "wx/univ/theme.h" 
  67 // ---------------------------------------------------------------------------- 
  69 // ---------------------------------------------------------------------------- 
  71 // the margin between the slider and the label (FIXME: hardcoded) 
  72 static const wxCoord SLIDER_LABEL_MARGIN 
= 2; 
  74 // ============================================================================ 
  75 // implementation of wxSlider 
  76 // ============================================================================ 
  78 IMPLEMENT_DYNAMIC_CLASS(wxSlider
, wxControl
) 
  80 BEGIN_EVENT_TABLE(wxSlider
, wxControl
) 
  81     EVT_SIZE(wxSlider::OnSize
) 
  84 // ---------------------------------------------------------------------------- 
  86 // ---------------------------------------------------------------------------- 
  89     // warning C4355: 'this' : used in base member initializer list 
  90     #pragma warning(disable:4355) 
  99 wxSlider::wxSlider(wxWindow 
*parent
, 
 101                    int value
, int minValue
, int maxValue
, 
 105                    const wxValidator
& validator
, 
 106                    const wxString
& name
) 
 111     (void)Create(parent
, id
, value
, minValue
, maxValue
, 
 112                  pos
, size
, style
, validator
, name
); 
 116     // warning C4355: 'this' : used in base member initializer list 
 117     #pragma warning(default:4355) 
 120 void wxSlider::Init() 
 135 bool wxSlider::Create(wxWindow 
*parent
, 
 137                       int value
, int minValue
, int maxValue
, 
 141                       const wxValidator
& validator
, 
 142                       const wxString
& name
) 
 144     if ( !wxSliderBase::Create(parent
, id
, pos
, size
, style
, 
 148     SetRange(minValue
, maxValue
); 
 151     // call this after setting the range as the best size depends (at least if 
 152     // we have wxSL_LABELS style) on the range 
 155     CreateInputHandler(wxINP_HANDLER_SLIDER
); 
 160 // ---------------------------------------------------------------------------- 
 161 // wxSlider range and value 
 162 // ---------------------------------------------------------------------------- 
 164 int wxSlider::GetValue() const 
 169 int wxSlider::NormalizeValue(int value
) const 
 173     else if ( value 
> m_max 
) 
 179 bool wxSlider::ChangeValueBy(int inc
) 
 181     return ChangeValueTo(NormalizeValue(m_value 
+ inc
)); 
 184 bool wxSlider::ChangeValueTo(int value
) 
 186     // this method is protected and we should only call it with normalized 
 188     wxCHECK_MSG( IsInRange(value
), FALSE
, _T("invalid slider value") ); 
 190     // check if the value is going to change at all 
 191     if ( value 
== m_value 
) 
 194     // refresh the old thumb position 
 202     // generate the event 
 203     wxCommandEvent 
event(wxEVT_COMMAND_SLIDER_UPDATED
, GetId()); 
 204     event
.SetInt(m_value
); 
 205     event
.SetEventObject(this); 
 207     (void)GetEventHandler()->ProcessEvent(event
); 
 212 void wxSlider::SetValue(int value
) 
 214     value 
= NormalizeValue(value
); 
 216     if ( m_value 
!= value 
) 
 224 void wxSlider::SetRange(int minValue
, int maxValue
) 
 226     if ( minValue 
> maxValue 
) 
 228         // swap them, we always want min to be less than max 
 234     if ( m_min 
!= minValue 
|| m_max 
!= maxValue 
) 
 239         // reset the value to make sure it is in the new range 
 242         // the size of the label rect might have changed 
 250     //else: nothing changed 
 253 int wxSlider::GetMin() const 
 258 int wxSlider::GetMax() const 
 263 // ---------------------------------------------------------------------------- 
 264 // wxSlider line/page/thumb size 
 265 // ---------------------------------------------------------------------------- 
 267 void wxSlider::SetLineSize(int lineSize
) 
 269     wxCHECK_RET( lineSize 
> 0, _T("invalid slider line size") ); 
 271     m_lineSize 
= lineSize
; 
 274 void wxSlider::SetPageSize(int pageSize
) 
 276     wxCHECK_RET( pageSize 
> 0, _T("invalid slider page size") ); 
 278     m_pageSize 
= pageSize
; 
 281 int wxSlider::GetLineSize() const 
 285         // the default line increment is 1 
 286         wxConstCast(this, wxSlider
)->m_lineSize 
= 1; 
 292 int wxSlider::GetPageSize() const 
 296         // the default page increment is 1/10 of the range 
 297         wxConstCast(this, wxSlider
)->m_pageSize 
= (m_max 
- m_min
) / 10; 
 303 void wxSlider::SetThumbLength(int lenPixels
) 
 305     wxCHECK_RET( lenPixels 
> 0, _T("invalid slider thumb size") ); 
 307     // use m_thumbSize here directly and not GetThumbLength() to avoid setting 
 308     // it to the default value as we don't need it 
 309     if ( lenPixels 
!= m_thumbSize 
) 
 311         m_thumbSize 
= lenPixels
; 
 317 int wxSlider::GetThumbLength() const 
 321         wxSize sz 
= GetDefaultThumbSize(); 
 322         return IsVert() ? sz
.y 
: sz
.x
; 
 328 // ---------------------------------------------------------------------------- 
 330 // ---------------------------------------------------------------------------- 
 332 void wxSlider::SetTickFreq(int n
, int WXUNUSED(dummy
)) 
 334     if ( n 
!= m_tickFreq 
) 
 342 // ---------------------------------------------------------------------------- 
 344 // ---------------------------------------------------------------------------- 
 346 wxSize 
wxSlider::CalcLabelSize() const 
 350     // there is no sense in trying to calc the labels size if we haven't got 
 351     // any, the caller must check for it 
 352     wxCHECK_MSG( HasLabels(), size
, _T("shouldn't be called") ); 
 354     wxCoord w1
, h1
, w2
, h2
; 
 355     GetTextExtent(FormatValue(m_min
), &w1
, &h1
); 
 356     GetTextExtent(FormatValue(m_max
), &w2
, &h2
); 
 358     size
.x 
= wxMax(w1
, w2
); 
 359     size
.y 
= wxMax(h1
, h2
); 
 364 wxSize 
wxSlider::DoGetBestClientSize() const 
 366     // this dimension is completely arbitrary 
 367     static const wxCoord SLIDER_WIDTH 
= 100; 
 369     // first calculate the size of the slider itself: i.e. the shaft and the 
 371     wxCoord height 
= GetRenderer()->GetSliderDim(); 
 377         size
.y 
= SLIDER_WIDTH
; 
 381         size
.x 
= SLIDER_WIDTH
; 
 385     // add space for ticks 
 388         wxCoord lenTick 
= GetRenderer()->GetSliderTickLen(); 
 396     // if we have the label, reserve enough space for it 
 399         wxSize sizeLabels 
= CalcLabelSize(); 
 402             size
.x 
+= sizeLabels
.x 
+ SLIDER_LABEL_MARGIN
; 
 404             size
.y 
+= sizeLabels
.y 
+ SLIDER_LABEL_MARGIN
; 
 410 void wxSlider::OnSize(wxSizeEvent
& event
) 
 417 const wxRect
& wxSlider::GetSliderRect() const 
 419     if ( m_rectSlider
.width 
< 0 ) 
 421         wxConstCast(this, wxSlider
)->CalcGeometry(); 
 427 void wxSlider::CalcGeometry() 
 430        recalc the label and slider positions, this looks like this for 
 431        wxSL_HORIZONTAL | wxSL_TOP slider: 
 434        ------------------------- 
 435        |                T      |  <-- this is the slider rect 
 436        | HHHHHHHHHHHHHHHTHHHHH | 
 439        ------------------------- 
 441        LLL is m_rectLabel as calculated here and lll is the real rect used for 
 442        label drawing in OnDraw() (TTT indicated the thumb position and *s are 
 445        in the wxSL_VERTICAL | wxSL_RIGHT case the picture is like this: 
 461     wxRect rectTotal 
= GetClientRect(); 
 464         wxSize sizeLabels 
= CalcLabelSize(); 
 466         m_rectSlider 
= rectTotal
; 
 467         m_rectLabel 
= wxRect(rectTotal
.GetPosition(), sizeLabels
); 
 472             sizeLabels
.x 
+= SLIDER_LABEL_MARGIN
; 
 474             if ( GetWindowStyle() & wxSL_LEFT 
) 
 476                 // shrink and offset the slider to the right 
 477                 m_rectSlider
.x 
+= sizeLabels
.x
; 
 478                 m_rectSlider
.width 
-= sizeLabels
.x
; 
 482                 // just shrink the slider and move the label to the right 
 483                 m_rectSlider
.width 
-= sizeLabels
.x
; 
 485                 m_rectLabel
.x 
+= m_rectSlider
.width 
+ SLIDER_LABEL_MARGIN
; 
 490             // same logic as above but x/y are trasnposed 
 491             sizeLabels
.y 
+= SLIDER_LABEL_MARGIN
; 
 493             if ( GetWindowStyle() & wxSL_TOP 
) 
 495                 m_rectSlider
.y 
+= sizeLabels
.y
; 
 496                 m_rectSlider
.height 
-= sizeLabels
.y
; 
 500                 m_rectSlider
.height 
-= sizeLabels
.y
; 
 502                 m_rectLabel
.y 
+= m_rectSlider
.height 
+ SLIDER_LABEL_MARGIN
; 
 508         // the slider takes the whole client rect 
 509         m_rectSlider 
= rectTotal
; 
 512     // now adjust for ticks too 
 515         wxCoord lenTick 
= GetRenderer()->GetSliderTickLen(); 
 519             m_rectSlider
.width 
-= lenTick
; 
 523             m_rectSlider
.height 
-= lenTick
; 
 526         // note that we must compute m_rectSlider first as GetShaftRect() uses 
 528         m_rectTicks 
= GetShaftRect(); 
 532             m_rectTicks
.x 
= m_rectSlider
.x 
+ m_rectSlider
.width
; 
 533             m_rectTicks
.width 
= lenTick
; 
 537             m_rectTicks
.y 
= m_rectSlider
.y 
+ m_rectSlider
.height
; 
 538             m_rectTicks
.height 
= lenTick
; 
 544 wxSize 
wxSlider::GetDefaultThumbSize() const 
 546     return GetRenderer()->GetSliderThumbSize(GetSliderRect(), GetOrientation()); 
 549 wxSize 
wxSlider::GetThumbSize() const 
 551     wxSize sizeThumb 
= GetDefaultThumbSize(); 
 553     // if we have our own thumb length (set by the user), use it instead of the 
 558             sizeThumb
.y 
= m_thumbSize
; 
 560             sizeThumb
.x 
= m_thumbSize
; 
 566 // ---------------------------------------------------------------------------- 
 567 // wxSlider thumb geometry 
 568 // ---------------------------------------------------------------------------- 
 570 wxRect 
wxSlider::GetShaftRect() const 
 572     return GetRenderer()->GetSliderShaftRect(m_rectSlider
, GetOrientation()); 
 575 void wxSlider::CalcThumbRect(const wxRect 
*rectShaftIn
, 
 576                              wxRect 
*rectThumbOut
, 
 577                              wxRect 
*rectLabelOut
, 
 580     if ( value 
== INVALID_THUMB_VALUE 
) 
 582         // use the current if not specified 
 586     bool isVertical 
= IsVert(); 
 591         rectShaft 
= *rectShaftIn
; 
 593     else // no shaft rect provided, calc it 
 595         rectShaft 
= GetShaftRect(); 
 601     wxRect 
rectThumb(rectShaft
.GetPosition(), GetThumbSize()); 
 604         rectThumb
.x 
+= (rectShaft
.width 
- rectThumb
.width
) / 2; 
 606         lenThumb 
= rectThumb
.height
; 
 607         lenShaft 
= rectShaft
.height
; 
 612         rectThumb
.y 
+= (rectShaft
.height 
- rectThumb
.height
) / 2; 
 614         lenThumb 
= rectThumb
.width
; 
 615         lenShaft 
= rectShaft
.width
; 
 619     // the thumb must always be entirely inside the shaft limits, so the max 
 620     // position is not at lenShaft but at lenShaft - thumbSize 
 621     if ( m_max 
!= m_min 
) 
 623         *p 
+= ((lenShaft 
- lenThumb
)*(value 
- m_min
))/(m_max 
- m_min
); 
 626     // calc the label rect 
 627     if ( HasLabels() && rectLabelOut 
) 
 629         wxRect rectLabel 
= m_rectLabel
; 
 631         // centre the label relatively to the thumb position 
 635                 rectThumb
.y 
+ (rectThumb
.height 
- m_rectLabel
.height
)/2; 
 640                 rectThumb
.x 
+ (rectThumb
.width 
- m_rectLabel
.width
)/2; 
 643         *rectLabelOut 
= rectLabel
; 
 647         *rectThumbOut 
= rectThumb
; 
 650 // ---------------------------------------------------------------------------- 
 652 // ---------------------------------------------------------------------------- 
 654 wxString 
wxSlider::FormatValue(int value
) const 
 656     return wxString::Format(_T("%d"), value
); 
 659 void wxSlider::DoDraw(wxControlRenderer 
*renderer
) 
 661     wxRenderer 
*rend 
= GetRenderer(); 
 662     wxDC
& dc 
= renderer
->GetDC(); 
 663     wxRect rectUpdate 
= GetUpdateClientRect(); 
 665     bool isVertical 
= IsVert(); 
 666     wxOrientation orient 
= GetOrientation(); 
 667     int flags 
= GetStateFlags(); 
 669     // first draw the shaft 
 670     wxRect rectShaft 
= rend
->GetSliderShaftRect(m_rectSlider
, orient
); 
 671     if ( rectUpdate
.Intersects(rectShaft
) ) 
 673         rend
->DrawSliderShaft(dc
, m_rectSlider
, orient
, flags
); 
 676     // calculate the thumb position in pixels and draw it 
 677     wxRect rectThumb
, rectLabel
; 
 678     CalcThumbRect(&rectShaft
, &rectThumb
, &rectLabel
); 
 680     if ( rectUpdate
.Intersects(rectThumb
) ) 
 682         rend
->DrawSliderThumb(dc
, rectThumb
, orient
, flags 
| m_thumbFlags
); 
 685     // then draw the ticks 
 686     if ( HasTicks() && rectUpdate
.Intersects(m_rectTicks
) ) 
 688         rend
->DrawSliderTicks(dc
, m_rectTicks
, rectThumb
.GetSize(), orient
, 
 689                               m_min
, m_max
, m_tickFreq
); 
 692     // finally, draw the label near the thumb 
 693     if ( HasLabels() && rectUpdate
.Intersects(rectLabel
) ) 
 695         // align it to be close to the shaft 
 699             align 
= wxALIGN_CENTRE_VERTICAL 
| 
 700                     (GetWindowStyle() & wxSL_RIGHT 
? wxALIGN_LEFT
 
 705             align 
= wxALIGN_CENTRE
; 
 708         dc
.SetFont(GetFont()); 
 709         dc
.SetTextForeground(GetForegroundColour()); 
 711         // the slider label is never drawn focused 
 712         rend
->DrawLabel(dc
, FormatValue(m_value
), rectLabel
, 
 713                         flags 
& ~wxCONTROL_FOCUSED
, align
); 
 717 void wxSlider::RefreshThumb() 
 719     wxRect rectThumb
, rectLabel
; 
 720     CalcThumbRect(NULL
, &rectThumb
, &rectLabel
); 
 722     Refresh(TRUE 
/* erase background */, &rectThumb
); 
 725         Refresh(TRUE
, &rectLabel
); 
 729 // ---------------------------------------------------------------------------- 
 730 // wxSlider input processing 
 731 // ---------------------------------------------------------------------------- 
 733 bool wxSlider::PerformAction(const wxControlAction
& action
, 
 735                              const wxString
& strArg
) 
 737     if ( action 
== wxACTION_SLIDER_START 
) 
 739         ChangeValueTo(m_min
); 
 741     else if ( action 
== wxACTION_SLIDER_END 
) 
 743         ChangeValueTo(m_max
); 
 745     else if ( action 
== wxACTION_SLIDER_PAGE_CHANGE 
) 
 747         ChangeValueBy(numArg 
* GetPageSize()); 
 749     else if ( action 
== wxACTION_SLIDER_LINE_UP 
) 
 751         ChangeValueBy(-GetLineSize()); 
 753     else if ( action 
== wxACTION_SLIDER_PAGE_UP 
) 
 755         return PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, -1); 
 757     else if ( action 
== wxACTION_SLIDER_LINE_DOWN 
) 
 759         ChangeValueBy(GetLineSize()); 
 761     else if ( action 
== wxACTION_SLIDER_PAGE_DOWN 
) 
 763         return PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, 1); 
 765     else if ( action 
== wxACTION_SLIDER_THUMB_DRAG 
) 
 767         // no special processing for it 
 770     else if ( action 
== wxACTION_SLIDER_THUMB_MOVE 
|| 
 771               action 
== wxACTION_SLIDER_THUMB_RELEASE 
) 
 773         ChangeValueTo((int)numArg
); 
 777         return wxControl::PerformAction(action
, numArg
, strArg
); 
 783 // ---------------------------------------------------------------------------- 
 784 // wxSlider implementation of wxControlWithThumb interface 
 785 // ---------------------------------------------------------------------------- 
 787 wxScrollThumb::Shaft 
wxSlider::HitTest(const wxPoint
& pt
) const 
 789     wxRect rectShaft 
= GetShaftRect(); 
 790     if ( !rectShaft
.Inside(pt
) ) 
 792         return wxScrollThumb::Shaft_None
; 
 795     // inside the shaft, where is it relatively to the thumb? 
 797     CalcThumbRect(&rectShaft
, &rectThumb
, NULL
); 
 799     // the position to test and the start and end of the thumb 
 804         x1 
= rectThumb
.GetTop(); 
 805         x2 
= rectThumb
.GetBottom(); 
 810         x1 
= rectThumb
.GetLeft(); 
 811         x2 
= rectThumb
.GetRight(); 
 817         return wxScrollThumb::Shaft_Above
; 
 823         return wxScrollThumb::Shaft_Below
; 
 826     // where else can it be? 
 827     return wxScrollThumb::Shaft_Thumb
; 
 830 wxCoord 
wxSlider::ThumbPosToPixel() const 
 833     CalcThumbRect(NULL
, &rectThumb
, NULL
); 
 835     return IsVert() ? rectThumb
.y 
: rectThumb
.x
; 
 838 int wxSlider::PixelToThumbPos(wxCoord x
) const 
 840     wxRect rectShaft 
= GetShaftRect(); 
 841     wxSize sizeThumb 
= GetThumbSize(); 
 847         len 
= rectShaft
.height 
- sizeThumb
.y
; 
 852         len 
= rectShaft
.width 
- sizeThumb
.x
; 
 860             pos 
+= ((x 
- x0
) * (m_max 
- m_min
)) / len
; 
 864         //else: x <= x0, leave pos = min 
 870 void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart
, 
 874     // for now we ignore the flags for the shaft as no renderer uses them 
 876     if ( shaftPart 
== wxScrollThumb::Shaft_Thumb 
) 
 879             m_thumbFlags 
|= flag
; 
 881             m_thumbFlags 
&= ~flag
; 
 887 void wxSlider::OnThumbDragStart(int pos
) 
 889     PerformAction(wxACTION_SLIDER_THUMB_DRAG
, pos
); 
 892 void wxSlider::OnThumbDrag(int pos
) 
 894     PerformAction(wxACTION_SLIDER_THUMB_MOVE
, pos
); 
 897 void wxSlider::OnThumbDragEnd(int pos
) 
 899     PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, pos
); 
 902 void wxSlider::OnPageScrollStart() 
 904     // we do nothing here 
 907 bool wxSlider::OnPageScroll(int pageInc
) 
 909     int value 
= GetValue(); 
 910     PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, pageInc
); 
 912     return GetValue() != value
; 
 915 // ---------------------------------------------------------------------------- 
 916 // wxStdSliderButtonInputHandler 
 917 // ---------------------------------------------------------------------------- 
 919 bool wxStdSliderButtonInputHandler::HandleKey(wxControl 
*control
, 
 920                                               const wxKeyEvent
& event
, 
 925         int keycode 
= event
.GetKeyCode(); 
 927         wxControlAction action
; 
 931                 action 
= wxACTION_SLIDER_START
; 
 935                 action 
= wxACTION_SLIDER_END
; 
 940                 action 
= wxACTION_SLIDER_LINE_UP
; 
 945                 action 
= wxACTION_SLIDER_LINE_DOWN
; 
 949                 action 
= wxACTION_SLIDER_PAGE_UP
; 
 953                 action 
= wxACTION_SLIDER_PAGE_DOWN
; 
 959             control
->PerformAction(action
); 
 965     return wxStdInputHandler::HandleKey(control
, event
, pressed
); 
 968 bool wxStdSliderButtonInputHandler::HandleMouse(wxControl 
*control
, 
 969                                                 const wxMouseEvent
& event
) 
 971     wxSlider 
*slider 
= wxStaticCast(control
, wxSlider
); 
 973     if ( slider
->GetThumb().HandleMouse(event
) ) 
 975         // processed by the thumb 
 979     return wxStdInputHandler::HandleMouse(control
, event
); 
 982 bool wxStdSliderButtonInputHandler::HandleMouseMove(wxControl 
*control
, 
 983                                                     const wxMouseEvent
& event
) 
 985     wxSlider 
*slider 
= wxStaticCast(control
, wxSlider
); 
 987     if ( slider
->GetThumb().HandleMouseMove(event
) ) 
 989         // processed by the thumb 
 993     return wxStdInputHandler::HandleMouseMove(control
, event
); 
 996 bool wxStdSliderButtonInputHandler::HandleFocus(wxControl 
*control
, 
 997                                                 const wxFocusEvent
& event
) 
 999     // slider's appearance changes when it gets/loses focus 
1003 #endif // wxUSE_SLIDER