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(wxInputConsumer
*consumer
,
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
;
950 action
= wxACTION_SLIDER_PAGE_UP
;
955 action
= wxACTION_SLIDER_PAGE_DOWN
;
961 consumer
->PerformAction(action
);
967 return wxStdInputHandler::HandleKey(consumer
, event
, pressed
);
970 bool wxStdSliderButtonInputHandler::HandleMouse(wxInputConsumer
*consumer
,
971 const wxMouseEvent
& event
)
973 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
975 if ( slider
->GetThumb().HandleMouse(event
) )
977 // processed by the thumb
981 return wxStdInputHandler::HandleMouse(consumer
, event
);
984 bool wxStdSliderButtonInputHandler::HandleMouseMove(wxInputConsumer
*consumer
,
985 const wxMouseEvent
& event
)
987 wxSlider
*slider
= wxStaticCast(consumer
->GetInputWindow(), wxSlider
);
989 if ( slider
->GetThumb().HandleMouseMove(event
) )
991 // processed by the thumb
995 return wxStdInputHandler::HandleMouseMove(consumer
, event
);
998 bool wxStdSliderButtonInputHandler::HandleFocus(wxInputConsumer
*consumer
,
999 const wxFocusEvent
& event
)
1001 // slider's appearance changes when it gets/loses focus
1005 #endif // wxUSE_SLIDER