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();
791 CalcThumbRect(&rectShaft
, &rectThumb
, NULL
);
793 // check for possible shaft or thumb hit
794 if (!rectShaft
.Inside(pt
) && !rectThumb
.Inside(pt
))
796 return wxScrollThumb::Shaft_None
;
800 // the position to test and the start and end of the thumb
805 x1
= rectThumb
.GetTop();
806 x2
= rectThumb
.GetBottom();
811 x1
= rectThumb
.GetLeft();
812 x2
= rectThumb
.GetRight();
818 return wxScrollThumb::Shaft_Above
;
824 return wxScrollThumb::Shaft_Below
;
827 // where else can it be?
828 return wxScrollThumb::Shaft_Thumb
;
831 wxCoord
wxSlider::ThumbPosToPixel() const
834 CalcThumbRect(NULL
, &rectThumb
, NULL
);
836 return IsVert() ? rectThumb
.y
: rectThumb
.x
;
839 int wxSlider::PixelToThumbPos(wxCoord x
) const
841 wxRect rectShaft
= GetShaftRect();
842 wxSize sizeThumb
= GetThumbSize();
848 len
= rectShaft
.height
- sizeThumb
.y
;
853 len
= rectShaft
.width
- sizeThumb
.x
;
861 pos
+= ((x
- x0
) * (m_max
- m_min
)) / len
;
865 //else: x <= x0, leave pos = min
871 void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart
,
875 // for now we ignore the flags for the shaft as no renderer uses them
877 if ( shaftPart
== wxScrollThumb::Shaft_Thumb
)
880 m_thumbFlags
|= flag
;
882 m_thumbFlags
&= ~flag
;
888 void wxSlider::OnThumbDragStart(int pos
)
890 PerformAction(wxACTION_SLIDER_THUMB_DRAG
, pos
);
893 void wxSlider::OnThumbDrag(int pos
)
895 PerformAction(wxACTION_SLIDER_THUMB_MOVE
, pos
);
898 void wxSlider::OnThumbDragEnd(int pos
)
900 PerformAction(wxACTION_SLIDER_THUMB_RELEASE
, pos
);
903 void wxSlider::OnPageScrollStart()
905 // we do nothing here
908 bool wxSlider::OnPageScroll(int pageInc
)
910 int value
= GetValue();
911 PerformAction(wxACTION_SLIDER_PAGE_CHANGE
, pageInc
);
913 return GetValue() != value
;
916 // ----------------------------------------------------------------------------
917 // wxStdSliderButtonInputHandler
918 // ----------------------------------------------------------------------------
920 bool wxStdSliderButtonInputHandler::HandleKey(wxInputConsumer
*consumer
,
921 const wxKeyEvent
& event
,
926 int keycode
= event
.GetKeyCode();
928 wxControlAction action
;
932 action
= wxACTION_SLIDER_START
;
936 action
= wxACTION_SLIDER_END
;
941 action
= wxACTION_SLIDER_LINE_UP
;
946 action
= wxACTION_SLIDER_LINE_DOWN
;
951 action
= wxACTION_SLIDER_PAGE_UP
;
956 action
= wxACTION_SLIDER_PAGE_DOWN
;
962 consumer
->PerformAction(action
);
968 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
971 bool wxStdSliderButtonInputHandler::HandleMouse(wxInputConsumer
*consumer
,
972 const wxMouseEvent
& event
)
974 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
976 if ( slider
->GetThumb().HandleMouse(event
) )
978 // processed by the thumb
982 return wxStdInputHandler::HandleMouse(consumer
, event
);
985 bool wxStdSliderButtonInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
986 const wxMouseEvent
& event
)
988 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
990 if ( slider
->GetThumb().HandleMouseMove(event
) )
992 // processed by the thumb
996 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
999 bool wxStdSliderButtonInputHandler::HandleFocus(wxInputConsumer
*consumer
,
1000 const wxFocusEvent
& event
)
1002 // slider's appearance changes when it gets/loses focus
1006 #endif // wxUSE_SLIDER