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 nothing at all, the current value only
26 or the value and the limits - the current approach is just that of the
27 lowest 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 // ----------------------------------------------------------------------------
45 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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();
605 wxRect
rectThumb(rectShaft
.GetPosition(), GetThumbSize());
608 rectThumb
.x
+= (rectShaft
.width
- rectThumb
.width
) / 2;
610 lenThumb
= rectThumb
.height
;
611 lenShaft
= rectShaft
.height
;
616 rectThumb
.y
+= (rectShaft
.height
- rectThumb
.height
) / 2;
618 lenThumb
= rectThumb
.width
;
619 lenShaft
= rectShaft
.width
;
623 // the thumb must always be entirely inside the shaft limits, so the max
624 // position is not at lenShaft but at lenShaft - thumbSize
625 if ( m_max
!= m_min
)
629 *p
+= ((lenShaft
- lenThumb
)*(m_max
- value
))/(m_max
- m_min
);
633 *p
+= ((lenShaft
- lenThumb
)*(value
- m_min
))/(m_max
- m_min
);
637 // calc the label rect
638 if ( HasLabels() && rectLabelOut
)
640 long style
= GetWindowStyle();
641 wxRect rectLabel
= m_rectLabel
;
643 // centre the label relatively to the thumb position
644 if (style
& (wxSL_TOP
|wxSL_BOTTOM
))
646 rectLabel
.x
= rectThumb
.x
+ (rectThumb
.width
- m_rectLabel
.width
)/2;
648 else if (style
& (wxSL_LEFT
|wxSL_RIGHT
))
650 rectLabel
.y
= rectThumb
.y
+ (rectThumb
.height
- m_rectLabel
.height
)/2;
653 *rectLabelOut
= rectLabel
;
658 *rectThumbOut
= rectThumb
;
661 // ----------------------------------------------------------------------------
663 // ----------------------------------------------------------------------------
665 wxString
wxSlider::FormatValue(int value
) const
667 return wxString::Format(_T("%d"), value
);
670 void wxSlider::DoDraw(wxControlRenderer
*renderer
)
672 wxRenderer
*rend
= GetRenderer();
673 wxDC
& dc
= renderer
->GetDC();
674 wxRect rectUpdate
= GetUpdateClientRect();
676 wxOrientation orient
= GetOrientation();
677 int flags
= GetStateFlags();
678 long style
= GetWindowStyle();
680 wxSize sz
= GetThumbSize();
681 int len
= IsVert() ? sz
.x
: sz
.y
;
683 // first draw the shaft
684 wxRect rectShaft
= rend
->GetSliderShaftRect(m_rectSlider
, len
, orient
, style
);
685 if ( rectUpdate
.Intersects(rectShaft
) )
687 rend
->DrawSliderShaft(dc
, m_rectSlider
, len
, orient
, flags
, style
);
690 // calculate the thumb position in pixels and draw it
691 wxRect rectThumb
, rectLabel
;
692 CalcThumbRect(&rectShaft
, &rectThumb
, &rectLabel
);
694 // then draw the ticks
695 if ( HasTicks() && rectUpdate
.Intersects(m_rectTicks
) )
697 rend
->DrawSliderTicks(dc
, m_rectSlider
, len
, orient
,
698 m_min
, m_max
, m_tickFreq
, flags
, style
);
701 // then draw the thumb
702 if ( rectUpdate
.Intersects(rectThumb
) )
704 rend
->DrawSliderThumb(dc
, rectThumb
, orient
, flags
| m_thumbFlags
, style
);
707 // finally, draw the label near the thumb
708 if ( HasLabels() && rectUpdate
.Intersects(rectLabel
) )
710 // align it to be close to the shaft
712 if (style
& wxSL_TOP
)
714 align
= wxALIGN_CENTRE_HORIZONTAL
|wxALIGN_TOP
;
716 else if (style
& wxSL_BOTTOM
)
718 align
= wxALIGN_CENTRE_HORIZONTAL
|wxALIGN_BOTTOM
;
720 else if (style
& wxSL_LEFT
)
722 align
= wxALIGN_CENTRE_VERTICAL
|wxALIGN_LEFT
;
724 else if (style
& wxSL_RIGHT
)
726 align
= wxALIGN_CENTRE_VERTICAL
|wxALIGN_RIGHT
;
729 dc
.SetFont(GetFont());
730 dc
.SetTextForeground(GetForegroundColour());
732 // the slider label is never drawn focused
733 rend
->DrawLabel(dc
, FormatValue(m_value
), rectLabel
,
734 flags
& ~wxCONTROL_FOCUSED
, align
);
738 // ----------------------------------------------------------------------------
739 // wxSlider input processing
740 // ----------------------------------------------------------------------------
742 bool wxSlider::PerformAction(const wxControlAction
& action
,
744 const wxString
& strArg
)
746 if ( action
== wxACTION_SLIDER_START
)
748 ChangeValueTo(m_min
);
750 else if ( action
== wxACTION_SLIDER_END
)
752 ChangeValueTo(m_max
);
754 else if ( action
== wxACTION_SLIDER_PAGE_CHANGE
)
756 ChangeValueBy(numArg
* GetPageSize());
758 else if ( action
== wxACTION_SLIDER_LINE_UP
)
760 ChangeValueBy(+GetLineSize());
762 else if ( action
== wxACTION_SLIDER_LINE_DOWN
)
764 ChangeValueBy(-GetLineSize());
766 else if ( action
== wxACTION_SLIDER_PAGE_UP
)
768 ChangeValueBy(+GetPageSize());
770 else if ( action
== wxACTION_SLIDER_PAGE_DOWN
)
772 ChangeValueBy(-GetPageSize());
774 else if ( action
== wxACTION_SLIDER_THUMB_DRAG
)
776 // no special processing for it
779 else if ( action
== wxACTION_SLIDER_THUMB_MOVE
||
780 action
== wxACTION_SLIDER_THUMB_RELEASE
)
782 ChangeValueTo((int)numArg
);
786 return wxControl::PerformAction(action
, numArg
, strArg
);
792 // ----------------------------------------------------------------------------
793 // wxSlider implementation of wxControlWithThumb interface
794 // ----------------------------------------------------------------------------
796 wxScrollThumb::Shaft
wxSlider::HitTest(const wxPoint
& pt
) const
798 wxRect rectShaft
= GetShaftRect();
800 CalcThumbRect(&rectShaft
, &rectThumb
, NULL
);
802 // check for possible shaft or thumb hit
803 if (!rectShaft
.Inside(pt
) && !rectThumb
.Inside(pt
))
805 return wxScrollThumb::Shaft_None
;
808 // the position to test and the start and end of the thumb
809 wxCoord x
, x1
, x2
, x3
, x4
;
813 x1
= rectThumb
.GetBottom();
814 x2
= rectShaft
.GetBottom();
815 x3
= rectShaft
.GetTop();
816 x4
= rectThumb
.GetTop();
821 x1
= rectShaft
.GetLeft();
822 x2
= rectThumb
.GetLeft();
823 x3
= rectThumb
.GetRight();
824 x4
= rectShaft
.GetRight();
826 if ((x1
<= x
) & (x
< x2
))
829 return wxScrollThumb::Shaft_Above
;
832 if ((x3
< x
) & (x
<= x4
)) {
834 return wxScrollThumb::Shaft_Below
;
837 // where else can it be?
838 return wxScrollThumb::Shaft_Thumb
;
841 wxCoord
wxSlider::ThumbPosToPixel() const
844 CalcThumbRect(NULL
, &rectThumb
, NULL
);
846 return IsVert() ? rectThumb
.y
: rectThumb
.x
;
849 int wxSlider::PixelToThumbPos(wxCoord x
) const
851 wxRect rectShaft
= GetShaftRect();
852 wxSize sizeThumb
= GetThumbSize();
858 len
= rectShaft
.height
- sizeThumb
.y
;
863 len
= rectShaft
.width
- sizeThumb
.x
;
871 pos
+= ((x
- x0
) * (m_max
- m_min
)) / len
;
875 //else: x <= x0, leave pos = min
881 void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart
,
885 // for now we ignore the flags for the shaft as no renderer uses them
887 if ( shaftPart
== wxScrollThumb::Shaft_Thumb
)
890 m_thumbFlags
|= flag
;
892 m_thumbFlags
&= ~flag
;
898 void wxSlider::OnThumbDragStart(int pos
)
902 PerformAction(wxACTION_SLIDER_THUMB_DRAG
, m_max
- pos
);
906 PerformAction(wxACTION_SLIDER_THUMB_DRAG
, pos
);
910 void wxSlider::OnThumbDrag(int pos
)
914 PerformAction(wxACTION_SLIDER_THUMB_MOVE
, m_max
- pos
);
918 PerformAction(wxACTION_SLIDER_THUMB_MOVE
, pos
);
922 void wxSlider::OnThumbDragEnd(int pos
)
926 PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, m_max
- pos
);
930 PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, pos
);
934 void wxSlider::OnPageScrollStart()
936 // we do nothing here
939 bool wxSlider::OnPageScroll(int pageInc
)
941 int value
= GetValue();
942 PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, pageInc
);
944 return GetValue() != value
;
947 // ----------------------------------------------------------------------------
948 // wxStdSliderButtonInputHandler
949 // ----------------------------------------------------------------------------
951 bool wxStdSliderButtonInputHandler::HandleKey(wxInputConsumer
*consumer
,
952 const wxKeyEvent
& event
,
957 int keycode
= event
.GetKeyCode();
959 wxControlAction action
;
963 action
= wxACTION_SLIDER_END
;
967 action
= wxACTION_SLIDER_START
;
972 action
= wxACTION_SLIDER_LINE_UP
;
977 action
= wxACTION_SLIDER_LINE_DOWN
;
982 action
= wxACTION_SLIDER_PAGE_UP
;
987 action
= wxACTION_SLIDER_PAGE_DOWN
;
991 if ( !action
.IsEmpty() )
993 consumer
->PerformAction(action
);
999 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
1002 bool wxStdSliderButtonInputHandler::HandleMouse(wxInputConsumer
*consumer
,
1003 const wxMouseEvent
& event
)
1005 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
1007 if ( slider
->GetThumb().HandleMouse(event
) )
1009 // processed by the thumb
1013 return wxStdInputHandler::HandleMouse(consumer
, event
);
1016 bool wxStdSliderButtonInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
1017 const wxMouseEvent
& event
)
1019 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
1021 if ( slider
->GetThumb().HandleMouseMove(event
) )
1023 // processed by the thumb
1027 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
1031 wxStdSliderButtonInputHandler::HandleFocus(wxInputConsumer
* WXUNUSED(consumer
),
1032 const wxFocusEvent
& WXUNUSED(event
))
1034 // slider's appearance changes when it gets/loses focus
1038 #endif // wxUSE_SLIDER