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 // check if the value is going to change at all
187 if (value
== m_value
) return FALSE
;
189 // this method is protected and we should only call it with normalized
191 wxCHECK_MSG( IsInRange(value
), FALSE
, _T("invalid slider value") );
197 // generate the event
198 wxCommandEvent
event(wxEVT_COMMAND_SLIDER_UPDATED
, GetId());
199 event
.SetInt(m_value
);
200 event
.SetEventObject(this);
202 (void)GetEventHandler()->ProcessEvent(event
);
207 void wxSlider::SetValue(int value
)
209 value
= NormalizeValue(value
);
211 if ( m_value
!= value
)
219 void wxSlider::SetRange(int minValue
, int maxValue
)
221 if ( minValue
> maxValue
)
223 // swap them, we always want min to be less than max
229 if ( m_min
!= minValue
|| m_max
!= maxValue
)
234 // reset the value to make sure it is in the new range
237 // the size of the label rect might have changed
245 //else: nothing changed
248 int wxSlider::GetMin() const
253 int wxSlider::GetMax() const
258 // ----------------------------------------------------------------------------
259 // wxSlider line/page/thumb size
260 // ----------------------------------------------------------------------------
262 void wxSlider::SetLineSize(int lineSize
)
264 wxCHECK_RET( lineSize
>= 0, _T("invalid slider line size") );
266 m_lineSize
= lineSize
;
269 void wxSlider::SetPageSize(int pageSize
)
271 wxCHECK_RET( pageSize
>= 0, _T("invalid slider page size") );
273 m_pageSize
= pageSize
;
276 int wxSlider::GetLineSize() const
280 // the default line increment is 1
281 wxConstCast(this, wxSlider
)->m_lineSize
= 1;
287 int wxSlider::GetPageSize() const
291 // the default page increment is m_tickFreq
292 wxConstCast(this, wxSlider
)->m_pageSize
= m_tickFreq
;
298 void wxSlider::SetThumbLength(int lenPixels
)
300 wxCHECK_RET( lenPixels
>= 0, _T("invalid slider thumb size") );
302 // use m_thumbSize here directly and not GetThumbLength() to avoid setting
303 // it to the default value as we don't need it
304 if ( lenPixels
!= m_thumbSize
)
306 m_thumbSize
= lenPixels
;
312 int wxSlider::GetThumbLength() const
314 wxSize sz
= GetDefaultThumbSize();
315 int len
= (IsVert() ? sz
.x
: sz
.y
);
316 if (m_thumbSize
> len
)
327 // ----------------------------------------------------------------------------
329 // ----------------------------------------------------------------------------
331 void wxSlider::SetTickFreq(int n
, int WXUNUSED(dummy
))
333 wxCHECK_RET (n
> 0, _T("invalid slider tick frequency"));
335 if ( n
!= m_tickFreq
)
343 // ----------------------------------------------------------------------------
345 // ----------------------------------------------------------------------------
347 wxSize
wxSlider::CalcLabelSize() const
351 // there is no sense in trying to calc the labels size if we haven't got
352 // any, the caller must check for it
353 wxCHECK_MSG( HasLabels(), size
, _T("shouldn't be called") );
355 wxCoord w1
, h1
, w2
, h2
;
356 GetTextExtent(FormatValue(m_min
), &w1
, &h1
);
357 GetTextExtent(FormatValue(m_max
), &w2
, &h2
);
359 size
.x
= wxMax(w1
, w2
);
360 size
.y
= wxMax(h1
, h2
);
365 wxSize
wxSlider::DoGetBestClientSize() const
367 // this dimension is completely arbitrary
368 static const wxCoord SLIDER_WIDTH
= 40;
370 long style
= GetWindowStyle();
372 // first calculate the size of the slider itself: i.e. the shaft and the
374 wxCoord height
= GetRenderer()->GetSliderDim();
380 size
.y
= SLIDER_WIDTH
;
384 size
.x
= SLIDER_WIDTH
;
388 // add space for ticks
391 wxCoord lenTick
= GetRenderer()->GetSliderTickLen();
392 if (style
& wxSL_BOTH
)
394 lenTick
= 2 * lenTick
;
403 // if we have the label, reserve enough space for it
406 wxSize sizeLabels
= CalcLabelSize();
408 if (style
& (wxSL_LEFT
|wxSL_RIGHT
))
410 size
.x
+= sizeLabels
.x
+ SLIDER_LABEL_MARGIN
;
412 else if (style
& (wxSL_TOP
|wxSL_BOTTOM
))
414 size
.y
+= sizeLabels
.y
+ SLIDER_LABEL_MARGIN
;
421 void wxSlider::OnSize(wxSizeEvent
& event
)
428 const wxRect
& wxSlider::GetSliderRect() const
430 if ( m_rectSlider
.width
< 0 )
432 wxConstCast(this, wxSlider
)->CalcGeometry();
438 void wxSlider::CalcGeometry()
441 recalc the label and slider positions, this looks like this for
442 wxSL_HORIZONTAL | wxSL_TOP slider:
445 -------------------------
446 | T | <-- this is the slider rect
447 | HHHHHHHHHHHHHHHTHHHHH |
450 -------------------------
452 LLL is m_rectLabel as calculated here and lll is the real rect used for
453 label drawing in OnDraw() (TTT indicated the thumb position and *s are
456 in the wxSL_VERTICAL | wxSL_RIGHT case the picture is like this:
471 long style
= GetWindowStyle();
473 // initialize to the full client rect
474 wxRect rectTotal
= GetClientRect();
475 m_rectSlider
= rectTotal
;
476 wxSize sizeThumb
= GetThumbSize();
478 // Labels reduce the size of the slider rect
481 wxSize sizeLabels
= CalcLabelSize();
483 m_rectLabel
= wxRect(rectTotal
.GetPosition(), sizeLabels
);
485 if (style
& wxSL_TOP
)
487 // shrink and offset the slider to the bottom
488 m_rectSlider
.y
+= sizeLabels
.y
+ SLIDER_LABEL_MARGIN
;
489 m_rectSlider
.height
-= sizeLabels
.y
+ SLIDER_LABEL_MARGIN
;
491 else if (style
& wxSL_BOTTOM
)
493 // shrink the slider and move the label to the bottom
494 m_rectSlider
.height
-= sizeLabels
.y
+ SLIDER_LABEL_MARGIN
;
495 m_rectLabel
.y
+= m_rectSlider
.height
+ SLIDER_LABEL_MARGIN
;
497 else if (style
& wxSL_LEFT
)
499 // shrink and offset the slider to the right
500 m_rectSlider
.x
+= sizeLabels
.x
+ SLIDER_LABEL_MARGIN
;
501 m_rectSlider
.width
-= sizeLabels
.x
+ SLIDER_LABEL_MARGIN
;
503 else if (style
& wxSL_RIGHT
)
505 // shrink the slider and move the label to the right
506 m_rectSlider
.width
-= sizeLabels
.x
+ SLIDER_LABEL_MARGIN
;
507 m_rectLabel
.x
+= m_rectSlider
.width
+ SLIDER_LABEL_MARGIN
;
511 // calculate ticks too
514 wxCoord lenTick
= GetRenderer()->GetSliderTickLen();
517 m_rectTicks
= GetShaftRect();
521 if (style
& (wxSL_LEFT
|wxSL_BOTH
))
523 m_rectTicks
.x
= m_rectSlider
.x
;
527 m_rectTicks
.x
= m_rectSlider
.x
+ m_rectSlider
.width
- lenTick
;
529 m_rectTicks
.width
= lenTick
;
533 if (style
& (wxSL_TOP
|wxSL_BOTH
))
535 m_rectTicks
.y
= m_rectSlider
.y
;
539 m_rectTicks
.y
= m_rectSlider
.y
+ m_rectSlider
.height
- lenTick
;
541 m_rectTicks
.height
= lenTick
;
545 // slider is never smaller than thumb size unless rectTotal
548 wxCoord width
= wxMin ( rectTotal
.width
, sizeThumb
.x
);
549 m_rectSlider
.width
= wxMax ( m_rectSlider
.width
, width
);
553 wxCoord height
= wxMin ( rectTotal
.height
, sizeThumb
.y
);
554 m_rectSlider
.height
= wxMax ( m_rectSlider
.height
, height
);
558 wxSize
wxSlider::GetDefaultThumbSize() const
560 // Default size has no styles (arrows)
561 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), 0, GetOrientation());
564 wxSize
wxSlider::GetThumbSize() const
566 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), m_thumbSize
, GetOrientation());
569 // ----------------------------------------------------------------------------
570 // wxSlider thumb geometry
571 // ----------------------------------------------------------------------------
573 wxRect
wxSlider::GetShaftRect() const
575 return GetRenderer()->GetSliderShaftRect(m_rectSlider
, m_thumbSize
, GetOrientation(), GetWindowStyle());
578 void wxSlider::CalcThumbRect(const wxRect
*rectShaftIn
,
579 wxRect
*rectThumbOut
,
580 wxRect
*rectLabelOut
,
583 if ( value
== INVALID_THUMB_VALUE
)
585 // use the current if not specified
589 bool isVertical
= IsVert();
594 rectShaft
= *rectShaftIn
;
596 else // no shaft rect provided, calc it
598 rectShaft
= GetShaftRect();
604 wxRect
rectThumb(rectShaft
.GetPosition(), GetThumbSize());
607 rectThumb
.x
+= (rectShaft
.width
- rectThumb
.width
) / 2;
609 lenThumb
= rectThumb
.height
;
610 lenShaft
= rectShaft
.height
;
615 rectThumb
.y
+= (rectShaft
.height
- rectThumb
.height
) / 2;
617 lenThumb
= rectThumb
.width
;
618 lenShaft
= rectShaft
.width
;
622 // the thumb must always be entirely inside the shaft limits, so the max
623 // position is not at lenShaft but at lenShaft - thumbSize
624 if ( m_max
!= m_min
)
628 *p
+= ((lenShaft
- lenThumb
)*(m_max
- value
))/(m_max
- m_min
);
632 *p
+= ((lenShaft
- lenThumb
)*(value
- m_min
))/(m_max
- m_min
);
636 // calc the label rect
637 if ( HasLabels() && rectLabelOut
)
639 long style
= GetWindowStyle();
640 wxRect rectLabel
= m_rectLabel
;
642 // centre the label relatively to the thumb position
643 if (style
& (wxSL_TOP
|wxSL_BOTTOM
))
645 rectLabel
.x
= rectThumb
.x
+ (rectThumb
.width
- m_rectLabel
.width
)/2;
647 else if (style
& (wxSL_LEFT
|wxSL_RIGHT
))
649 rectLabel
.y
= rectThumb
.y
+ (rectThumb
.height
- m_rectLabel
.height
)/2;
652 *rectLabelOut
= rectLabel
;
657 *rectThumbOut
= rectThumb
;
660 // ----------------------------------------------------------------------------
662 // ----------------------------------------------------------------------------
664 wxString
wxSlider::FormatValue(int value
) const
666 return wxString::Format(_T("%d"), value
);
669 void wxSlider::DoDraw(wxControlRenderer
*renderer
)
671 wxRenderer
*rend
= GetRenderer();
672 wxDC
& dc
= renderer
->GetDC();
673 wxRect rectUpdate
= GetUpdateClientRect();
675 wxOrientation orient
= GetOrientation();
676 int flags
= GetStateFlags();
677 long style
= GetWindowStyle();
679 wxSize sz
= GetThumbSize();
680 int len
= IsVert() ? sz
.x
: sz
.y
;
682 // first draw the shaft
683 wxRect rectShaft
= rend
->GetSliderShaftRect(m_rectSlider
, len
, orient
, style
);
684 if ( rectUpdate
.Intersects(rectShaft
) )
686 rend
->DrawSliderShaft(dc
, m_rectSlider
, len
, orient
, flags
, style
);
689 // calculate the thumb position in pixels and draw it
690 wxRect rectThumb
, rectLabel
;
691 CalcThumbRect(&rectShaft
, &rectThumb
, &rectLabel
);
693 // then draw the ticks
694 if ( HasTicks() && rectUpdate
.Intersects(m_rectTicks
) )
696 rend
->DrawSliderTicks(dc
, m_rectSlider
, len
, orient
,
697 m_min
, m_max
, m_tickFreq
, flags
, style
);
700 // then draw the thumb
701 if ( rectUpdate
.Intersects(rectThumb
) )
703 rend
->DrawSliderThumb(dc
, rectThumb
, orient
, flags
| m_thumbFlags
, style
);
706 // finally, draw the label near the thumb
707 if ( HasLabels() && rectUpdate
.Intersects(rectLabel
) )
709 // align it to be close to the shaft
711 if (style
& wxSL_TOP
)
713 align
= wxALIGN_CENTRE_HORIZONTAL
|wxALIGN_TOP
;
715 else if (style
& wxSL_BOTTOM
)
717 align
= wxALIGN_CENTRE_HORIZONTAL
|wxALIGN_BOTTOM
;
719 else if (style
& wxSL_LEFT
)
721 align
= wxALIGN_CENTRE_VERTICAL
|wxALIGN_LEFT
;
723 else if (style
& wxSL_RIGHT
)
725 align
= wxALIGN_CENTRE_VERTICAL
|wxALIGN_RIGHT
;
728 dc
.SetFont(GetFont());
729 dc
.SetTextForeground(GetForegroundColour());
731 // the slider label is never drawn focused
732 rend
->DrawLabel(dc
, FormatValue(m_value
), rectLabel
,
733 flags
& ~wxCONTROL_FOCUSED
, align
);
737 // ----------------------------------------------------------------------------
738 // wxSlider input processing
739 // ----------------------------------------------------------------------------
741 bool wxSlider::PerformAction(const wxControlAction
& action
,
743 const wxString
& strArg
)
745 if ( action
== wxACTION_SLIDER_START
)
747 ChangeValueTo(m_min
);
749 else if ( action
== wxACTION_SLIDER_END
)
751 ChangeValueTo(m_max
);
753 else if ( action
== wxACTION_SLIDER_PAGE_CHANGE
)
755 ChangeValueBy(numArg
* GetPageSize());
757 else if ( action
== wxACTION_SLIDER_LINE_UP
)
759 ChangeValueBy(+GetLineSize());
761 else if ( action
== wxACTION_SLIDER_LINE_DOWN
)
763 ChangeValueBy(-GetLineSize());
765 else if ( action
== wxACTION_SLIDER_PAGE_UP
)
767 ChangeValueBy(+GetPageSize());
769 else if ( action
== wxACTION_SLIDER_PAGE_DOWN
)
771 ChangeValueBy(-GetPageSize());
773 else if ( action
== wxACTION_SLIDER_THUMB_DRAG
)
775 // no special processing for it
778 else if ( action
== wxACTION_SLIDER_THUMB_MOVE
||
779 action
== wxACTION_SLIDER_THUMB_RELEASE
)
781 ChangeValueTo((int)numArg
);
785 return wxControl::PerformAction(action
, numArg
, strArg
);
791 // ----------------------------------------------------------------------------
792 // wxSlider implementation of wxControlWithThumb interface
793 // ----------------------------------------------------------------------------
795 wxScrollThumb::Shaft
wxSlider::HitTest(const wxPoint
& pt
) const
797 wxRect rectShaft
= GetShaftRect();
799 CalcThumbRect(&rectShaft
, &rectThumb
, NULL
);
801 // check for possible shaft or thumb hit
802 if (!rectShaft
.Inside(pt
) && !rectThumb
.Inside(pt
))
804 return wxScrollThumb::Shaft_None
;
807 // the position to test and the start and end of the thumb
808 wxCoord x
, x1
, x2
, x3
, x4
;
812 x1
= rectThumb
.GetBottom();
813 x2
= rectShaft
.GetBottom();
814 x3
= rectShaft
.GetTop();
815 x4
= rectThumb
.GetTop();
820 x1
= rectShaft
.GetLeft();
821 x2
= rectThumb
.GetLeft();
822 x3
= rectThumb
.GetRight();
823 x4
= rectShaft
.GetRight();
825 if ((x1
<= x
) & (x
< x2
))
828 return wxScrollThumb::Shaft_Above
;
831 if ((x3
< x
) & (x
<= x4
)) {
833 return wxScrollThumb::Shaft_Below
;
836 // where else can it be?
837 return wxScrollThumb::Shaft_Thumb
;
840 wxCoord
wxSlider::ThumbPosToPixel() const
843 CalcThumbRect(NULL
, &rectThumb
, NULL
);
845 return IsVert() ? rectThumb
.y
: rectThumb
.x
;
848 int wxSlider::PixelToThumbPos(wxCoord x
) const
850 wxRect rectShaft
= GetShaftRect();
851 wxSize sizeThumb
= GetThumbSize();
857 len
= rectShaft
.height
- sizeThumb
.y
;
862 len
= rectShaft
.width
- sizeThumb
.x
;
870 pos
+= ((x
- x0
) * (m_max
- m_min
)) / len
;
874 //else: x <= x0, leave pos = min
880 void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart
,
884 // for now we ignore the flags for the shaft as no renderer uses them
886 if ( shaftPart
== wxScrollThumb::Shaft_Thumb
)
889 m_thumbFlags
|= flag
;
891 m_thumbFlags
&= ~flag
;
897 void wxSlider::OnThumbDragStart(int pos
)
901 PerformAction(wxACTION_SLIDER_THUMB_DRAG
, m_max
- pos
);
905 PerformAction(wxACTION_SLIDER_THUMB_DRAG
, pos
);
909 void wxSlider::OnThumbDrag(int pos
)
913 PerformAction(wxACTION_SLIDER_THUMB_MOVE
, m_max
- pos
);
917 PerformAction(wxACTION_SLIDER_THUMB_MOVE
, pos
);
921 void wxSlider::OnThumbDragEnd(int pos
)
925 PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, m_max
- pos
);
929 PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, pos
);
933 void wxSlider::OnPageScrollStart()
935 // we do nothing here
938 bool wxSlider::OnPageScroll(int pageInc
)
940 int value
= GetValue();
941 PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, pageInc
);
943 return GetValue() != value
;
946 // ----------------------------------------------------------------------------
947 // wxStdSliderButtonInputHandler
948 // ----------------------------------------------------------------------------
950 bool wxStdSliderButtonInputHandler::HandleKey(wxInputConsumer
*consumer
,
951 const wxKeyEvent
& event
,
956 int keycode
= event
.GetKeyCode();
958 wxControlAction action
;
962 action
= wxACTION_SLIDER_END
;
966 action
= wxACTION_SLIDER_START
;
971 action
= wxACTION_SLIDER_LINE_UP
;
976 action
= wxACTION_SLIDER_LINE_DOWN
;
981 action
= wxACTION_SLIDER_PAGE_UP
;
986 action
= wxACTION_SLIDER_PAGE_DOWN
;
992 consumer
->PerformAction(action
);
998 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1001 bool wxStdSliderButtonInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1002 const wxMouseEvent
& event
)
1004 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
1006 if ( slider
->GetThumb().HandleMouse(event
) )
1008 // processed by the thumb
1012 return wxStdInputHandler::HandleMouse(consumer
, event
);
1015 bool wxStdSliderButtonInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1016 const wxMouseEvent
& event
)
1018 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
1020 if ( slider
->GetThumb().HandleMouseMove(event
) )
1022 // processed by the thumb
1026 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1030 wxStdSliderButtonInputHandler::HandleFocus(wxInputConsumer
* WXUNUSED(consumer
),
1031 const wxFocusEvent
& WXUNUSED(event
))
1033 // slider's appearance changes when it gets/loses focus
1037 #endif // wxUSE_SLIDER