diff --git a/include/wx/univ/control.h b/include/wx/univ/control.h index 12c5a5abf0..45acd4fbdb 100644 --- a/include/wx/univ/control.h +++ b/include/wx/univ/control.h @@ -20,6 +20,7 @@ class WXDLLEXPORT wxControlRenderer; class WXDLLEXPORT wxInputHandler; +class WXDLLEXPORT wxRenderer; // ---------------------------------------------------------------------------- // constants @@ -143,6 +144,10 @@ public: const wxEvent& event); protected: + // returns the (low level) renderer to use for drawing the control by + // querying the current theme + wxRenderer *GetRenderer() const; + // create the event translator object for this control: the base class // action creates the default one which doesn't do anything virtual wxInputHandler *CreateInputHandler() const; diff --git a/include/wx/univ/inphand.h b/include/wx/univ/inphand.h index c12ff14273..bb846f621f 100644 --- a/include/wx/univ/inphand.h +++ b/include/wx/univ/inphand.h @@ -19,6 +19,9 @@ #include "wx/control.h" // for wxControlAction(s) +class WXDLLEXPORT wxRenderer; +class WXDLLEXPORT wxScrollBar; + // ---------------------------------------------------------------------------- // wxInputHandler: maps the events to the actions // ---------------------------------------------------------------------------- @@ -124,6 +127,8 @@ public: const wxMouseEvent& event); virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event); + virtual ~wxStdScrollBarInputHandler(); + protected: // the methods which must be overridden in the derived class @@ -159,6 +164,10 @@ protected: // the renderer (we use it only for hit testing) wxRenderer *m_renderer; + + // the timer for generating scroll events when the mouse stays pressed on + // a scrollbar + class wxScrollBarTimer *m_timerScroll; }; #endif // _WX_UNIV_INPHAND_H_ diff --git a/include/wx/univ/renderer.h b/include/wx/univ/renderer.h index acbddcc249..9edea806cd 100644 --- a/include/wx/univ/renderer.h +++ b/include/wx/univ/renderer.h @@ -134,9 +134,15 @@ public: // --------------------- // returns one of wxHT_SCROLLBAR_XXX constants - virtual wxHitTest HitTestScrollbar(wxScrollBar *scrollbar, + virtual wxHitTest HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const = 0; + // translate the scrollbar position (in logical units) into physical + // coordinate (in pixels) and the other way round + virtual wxCoord ScrollbarToPixel(const wxScrollBar *scrollbar) = 0; + virtual int PixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord) = 0; + // virtual dtor for any base class virtual ~wxRenderer(); @@ -144,9 +150,16 @@ protected: // standard scrollbar hit testing: this assumes that it only has 2 arrows // and a thumb, so the themes which have more complicated scrollbars (e.g. // BeOS) can't use this method - static wxHitTest StandardHitTestScrollbar(wxScrollBar *scrollbar, + static wxHitTest StandardHitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt, const wxSize& sizeArrow); + static wxCoord StandardScrollbarToPixel(const wxScrollBar *scrollbar, + const wxSize& sizeArrow); + static int StandardPixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord, + const wxSize& sizeArrow); + static wxCoord StandardScrollBarSize(const wxScrollBar *scrollbar, + const wxSize& sizeArrow); }; // ---------------------------------------------------------------------------- @@ -207,9 +220,14 @@ public: virtual void AdjustSize(wxSize *size, const wxWindow *window) { m_renderer->AdjustSize(size, window); } - virtual wxHitTest HitTestScrollbar(wxScrollBar *scrollbar, + virtual wxHitTest HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const { return m_renderer->HitTestScrollbar(scrollbar, pt); } + virtual wxCoord ScrollbarToPixel(const wxScrollBar *scrollbar) + { return m_renderer->ScrollbarToPixel(scrollbar); } + virtual int PixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord) + { return m_renderer->PixelToScrollbar(scrollbar, coord); } protected: wxRenderer *m_renderer; @@ -232,7 +250,7 @@ public: void DrawButtonBorder(); void DrawFrame(); void DrawBackgroundBitmap(); - void DrawScrollbar(wxScrollBar *scrollbar); + void DrawScrollbar(const wxScrollBar *scrollbar); // accessors wxRenderer *GetRenderer() const { return m_renderer; } diff --git a/include/wx/univ/scrolbar.h b/include/wx/univ/scrolbar.h index b706d0dd1f..12e021b597 100644 --- a/include/wx/univ/scrolbar.h +++ b/include/wx/univ/scrolbar.h @@ -52,7 +52,7 @@ public: Element_Arrow_Line_2, Element_Arrow_Page_1, Element_Arrow_Page_2, - Element_Arrow_Thumb, + Element_Thumb, Element_Bar_1, Element_Bar_2, Element_Max @@ -117,6 +117,10 @@ protected: void Init(); private: + // get the mouse coordinates in the scrollbar direction from a + // wxMouseEvent (the event *must* really be of this type!) + wxCoord GetMouseCoord(const wxEvent& event) const; + // total range of the scrollbar in logical units int m_range; @@ -132,6 +136,10 @@ private: // the state of the sub elements int m_elementsState[Element_Max]; + // the offset of the top/left of the scrollbar relative to the mouse to + // keep during the thumb drag + int m_ofsMouse; + DECLARE_DYNAMIC_CLASS(wxScrollBar) }; diff --git a/samples/univ/univ.cpp b/samples/univ/univ.cpp index 23cb613fe5..af21394bbd 100644 --- a/samples/univ/univ.cpp +++ b/samples/univ/univ.cpp @@ -118,7 +118,7 @@ bool MyUnivApp::OnInit() // ---------------------------------------------------------------------------- MyUnivFrame::MyUnivFrame(const wxString& title) - : wxFrame(NULL, -1, title, wxDefaultPosition, wxSize(600, 450)) + : wxFrame(NULL, -1, title, wxDefaultPosition, wxSize(600, 600)) { #ifdef __WXMSW__ SetBackgroundColour(*wxLIGHT_GREY); @@ -181,9 +181,9 @@ MyUnivFrame::MyUnivFrame(const wxString& title) new wxButton(this, Univ_Button2, _T("&And me"), wxPoint(100, 300)); wxScrollBar *sb; - sb = new wxScrollBar(this, -1, wxPoint(200, 300), wxSize(100, -1)); + sb = new wxScrollBar(this, -1, wxPoint(200, 300), wxSize(300, -1)); sb->SetScrollbar(0, 10, 100, 10); - sb = new wxScrollBar(this, -1, wxPoint(200, 330), wxSize(-1, 50), wxSB_VERTICAL); + sb = new wxScrollBar(this, -1, wxPoint(200, 330), wxSize(-1, 150), wxSB_VERTICAL); sb->SetScrollbar(50, 50, 100, 10); } diff --git a/src/gtk/files.lst b/src/gtk/files.lst index 4f6ed3adec..9708c4099e 100644 --- a/src/gtk/files.lst +++ b/src/gtk/files.lst @@ -993,6 +993,7 @@ GUI_LOWLEVEL_OBJS = \ pen.o \ region.o \ settings.o \ + timer.o \ utilsgtk.o \ win_gtk.o \ window.o @@ -1018,6 +1019,7 @@ GUI_LOWLEVEL_DEPS = \ pen.d \ region.d \ settings.d \ + timer.d \ utilsgtk.d \ win_gtk.d \ window.d diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 3f5e8f73b6..b447e196fe 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -1437,8 +1437,6 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, // the mouse changed window g_captureWindowHasMouse = hasMouse; - printf("Generating mouse %s event.\n", - g_captureWindowHasMouse ? "enter" : "leave"); wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW : wxEVT_LEAVE_WINDOW); InitMouseEvent(event, gdk_event); diff --git a/src/gtk1/files.lst b/src/gtk1/files.lst index 4f6ed3adec..9708c4099e 100644 --- a/src/gtk1/files.lst +++ b/src/gtk1/files.lst @@ -993,6 +993,7 @@ GUI_LOWLEVEL_OBJS = \ pen.o \ region.o \ settings.o \ + timer.o \ utilsgtk.o \ win_gtk.o \ window.o @@ -1018,6 +1019,7 @@ GUI_LOWLEVEL_DEPS = \ pen.d \ region.d \ settings.d \ + timer.d \ utilsgtk.d \ win_gtk.d \ window.d diff --git a/src/gtk1/window.cpp b/src/gtk1/window.cpp index 3f5e8f73b6..b447e196fe 100644 --- a/src/gtk1/window.cpp +++ b/src/gtk1/window.cpp @@ -1437,8 +1437,6 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, // the mouse changed window g_captureWindowHasMouse = hasMouse; - printf("Generating mouse %s event.\n", - g_captureWindowHasMouse ? "enter" : "leave"); wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW : wxEVT_LEAVE_WINDOW); InitMouseEvent(event, gdk_event); diff --git a/src/univ/control.cpp b/src/univ/control.cpp index 449d95c333..fb362cff44 100644 --- a/src/univ/control.cpp +++ b/src/univ/control.cpp @@ -217,6 +217,11 @@ const wxBitmap& wxControl::GetBackgroundBitmap(int *alignment, // painting // ---------------------------------------------------------------------------- +wxRenderer *wxControl::GetRenderer() const +{ + return wxTheme::Get()->GetRenderer(); +} + // the event handler executed when the window background must be painted void wxControl::OnErase(wxEraseEvent& event) { @@ -235,7 +240,7 @@ void wxControl::OnPaint(wxPaintEvent& event) { // get the DC to use and create renderer on it wxPaintDC dc(this); - wxControlRenderer renderer(this, dc, wxTheme::Get()->GetRenderer()); + wxControlRenderer renderer(this, dc, GetRenderer()); // do draw the control! DoDraw(&renderer); diff --git a/src/univ/inphand.cpp b/src/univ/inphand.cpp index 17a8ad6542..1dfd42102a 100644 --- a/src/univ/inphand.cpp +++ b/src/univ/inphand.cpp @@ -29,14 +29,73 @@ #endif #ifndef WX_PRECOMP + #include "wx/timer.h" + + #include "wx/button.h" + #include "wx/scrolbar.h" + #include "wx/univ/renderer.h" #endif // WX_PRECOMP #include "wx/univ/inphand.h" +// ---------------------------------------------------------------------------- +// wxScrollBarTimer: this class is used to repeatedly scroll the scrollbar +// when the mouse is help pressed on the arrow or on the bar. It generates the +// given scroll action command periodically. +// ---------------------------------------------------------------------------- + +class wxScrollBarTimer : public wxTimer +{ +public: + wxScrollBarTimer(const wxControlAction& action, + const wxMouseEvent& event, + wxControl *control); + + virtual void Notify(); + +private: + wxControlAction m_action; + wxMouseEvent m_event; + wxControl *m_control; +}; + // ============================================================================ // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// wxScrollBarTimer +// ---------------------------------------------------------------------------- + +wxScrollBarTimer::wxScrollBarTimer(const wxControlAction& action, + const wxMouseEvent& event, + wxControl *control) + : m_event(event) +{ + m_action = action; + m_control = control; + + // start scrolling immediately + Notify(); + + // and continue it later + Start(100); // FIXME make this delay configurable +} + +void wxScrollBarTimer::Notify() +{ + if ( m_control->PerformAction(m_action, m_event) ) + { + // keep scrolling + m_control->Refresh(); + } + else + { + // we scrolled till the end + Stop(); + } +} + // ---------------------------------------------------------------------------- // wxInputHandler // ---------------------------------------------------------------------------- @@ -163,6 +222,14 @@ wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer, m_renderer = renderer; m_winCapture = NULL; m_htLast = wxHT_NOWHERE; + m_timerScroll = NULL; +} + +wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler() +{ + // normally, it's NULL by now but just in case the user somehow managed to + // keep the mouse captured until now... + delete m_timerScroll; } void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control, @@ -266,21 +333,35 @@ wxControlActions wxStdScrollBarInputHandler::Map(wxControl *control, action = wxACTION_SCROLL_PAGE_DOWN; break; + case wxHT_SCROLLBAR_THUMB: + control->PerformAction(wxACTION_SCROLL_THUMB_DRAG, event); + // fall through: there is no immediate action + default: hasAction = FALSE; } - if ( hasAction ) - { - control->PerformAction(action, event); - } - - // remove highlighting and press the arrow instead + // remove highlighting Highlight(scrollbar, FALSE); m_htLast = ht; - Press(scrollbar, TRUE); - control->Refresh(); + // and press the arrow or highlight thumb now instead + if ( m_htLast == wxHT_SCROLLBAR_THUMB ) + Highlight(scrollbar, TRUE); + else + Press(scrollbar, TRUE); + + // start dragging + if ( hasAction ) + { + m_timerScroll = new wxScrollBarTimer(action, event, control); + } + else // no (immediate) action + { + // highlighting still might have changed + control->Refresh(); + } + } //else: mouse already captured, nothing to do } @@ -289,10 +370,23 @@ wxControlActions wxStdScrollBarInputHandler::Map(wxControl *control, { if ( m_winCapture ) { + // return everything to the normal state m_winCapture->ReleaseMouse(); m_winCapture = NULL; m_btnCapture = -1; + if ( m_timerScroll ) + { + delete m_timerScroll; + m_timerScroll = NULL; + } + + // if we were dragging the thumb, send the last event + if ( m_htLast == wxHT_SCROLLBAR_THUMB ) + { + control->PerformAction(wxACTION_SCROLL_THUMB_RELEASE, event); + } + // unpress the arrow and highlight the current element Press(scrollbar, FALSE); m_htLast = ht; @@ -318,8 +412,14 @@ bool wxStdScrollBarInputHandler::OnMouseMove(wxControl *control, { if ( m_winCapture ) { - // everything is locked while the mouse is captured, so don't do - // anything + if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Moving() ) + { + // drag the thumb so that it follows the mouse + if ( control->PerformAction(wxACTION_SCROLL_THUMB_MOVE, event) ) + return TRUE; + } + + // no other changes are possible while the mouse is captured return FALSE; } diff --git a/src/univ/renderer.cpp b/src/univ/renderer.cpp index ba08495a96..a087eaf85a 100644 --- a/src/univ/renderer.cpp +++ b/src/univ/renderer.cpp @@ -44,11 +44,54 @@ // ============================================================================ // ---------------------------------------------------------------------------- -// wxRenderer +// wxRenderer: scrollbar geometry // ---------------------------------------------------------------------------- /* static */ -wxHitTest wxRenderer::StandardHitTestScrollbar(wxScrollBar *scrollbar, +wxCoord wxRenderer::StandardScrollBarSize(const wxScrollBar *scrollbar, + const wxSize& sizeArrowSB) +{ + wxCoord sizeArrow, sizeTotal; + if ( scrollbar->GetWindowStyle() & wxVERTICAL ) + { + sizeArrow = sizeArrowSB.y; + sizeTotal = scrollbar->GetSize().y; + } + else // horizontal + { + sizeArrow = sizeArrowSB.x; + sizeTotal = scrollbar->GetSize().x; + } + + return sizeTotal - 2*sizeArrow; +} + +/* static */ +wxCoord wxRenderer::StandardScrollbarToPixel(const wxScrollBar *scrollbar, + const wxSize& sizeArrow) +{ + int range = scrollbar->GetRange(); + if ( !range ) + { + // the only valid position anyhow + return 0; + } + + return ( scrollbar->GetThumbPosition() * + StandardScrollBarSize(scrollbar, sizeArrow) ) / range; +} + +/* static */ +int wxRenderer::StandardPixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord, + const wxSize& sizeArrow) +{ + return ( coord * scrollbar->GetRange() ) + / StandardScrollBarSize(scrollbar, sizeArrow); +} + +/* static */ +wxHitTest wxRenderer::StandardHitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt, const wxSize& sizeArrowSB) { @@ -242,7 +285,7 @@ void wxControlRenderer::DrawBackgroundBitmap() m_dc.DrawBitmap(bmp, x, y); } -void wxControlRenderer::DrawScrollbar(wxScrollBar *scrollbar) +void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar) { int thumbStart, thumbEnd; int range = scrollbar->GetRange(); diff --git a/src/univ/scrolbar.cpp b/src/univ/scrolbar.cpp index 056f76d146..cfe348f1ca 100644 --- a/src/univ/scrolbar.cpp +++ b/src/univ/scrolbar.cpp @@ -180,10 +180,27 @@ void wxScrollBar::DoDraw(wxControlRenderer *renderer) // input processing // ---------------------------------------------------------------------------- +wxCoord wxScrollBar::GetMouseCoord(const wxEvent& eventOrig) const +{ + const wxMouseEvent& event = (const wxMouseEvent&)eventOrig; + wxPoint pt = event.GetPosition(); + return GetWindowStyle() & wxVERTICAL ? pt.y : pt.x; +} + bool wxScrollBar::PerformAction(const wxControlAction& action, const wxEvent& event) { - if ( action == wxACTION_SCROLL_START ) + int thumbOld = m_thumbPos; + + // test for thumb move first as these events happen in quick succession + if ( action == wxACTION_SCROLL_THUMB_MOVE ) + { + // make the thumb follow the mouse by keeping the same offset between + // the mouse position and the top/left of the thumb + int thumbPos = GetMouseCoord(event) - m_ofsMouse; + DoSetThumb(GetRenderer()->PixelToScrollbar(this, thumbPos)); + } + else if ( action == wxACTION_SCROLL_START ) ScrollToStart(); else if ( action == wxACTION_SCROLL_END ) ScrollToEnd(); @@ -195,11 +212,18 @@ bool wxScrollBar::PerformAction(const wxControlAction& action, ScrollPages(-1); else if ( action == wxACTION_SCROLL_PAGE_DOWN ) ScrollPages(1); + else if ( action == wxACTION_SCROLL_THUMB_DRAG ) + { + m_ofsMouse = GetMouseCoord(event) - + GetRenderer()->ScrollbarToPixel(this); + } + else if ( action == wxACTION_SCROLL_THUMB_RELEASE ) + ; // nothing special to do else return wxControl::PerformAction(action, event); - // scrollbar position changed - update - return TRUE; + // if scrollbar position changed - update + return m_thumbPos != thumbOld; } void wxScrollBar::ScrollToStart() diff --git a/src/univ/themes/gtk.cpp b/src/univ/themes/gtk.cpp index 8f883308ce..578bdf9130 100644 --- a/src/univ/themes/gtk.cpp +++ b/src/univ/themes/gtk.cpp @@ -90,8 +90,10 @@ public: virtual void AdjustSize(wxSize *size, const wxWindow *window); // hit testing for the input handlers - virtual wxHitTest HitTestScrollbar(wxScrollBar *scrollbar, + virtual wxHitTest HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const; + virtual wxCoord ScrollbarToPixel(const wxScrollBar *scrollbar); + virtual int PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord); protected: // DrawBackground() helpers @@ -773,9 +775,9 @@ void wxGTKRenderer::DrawScrollbar(wxDC& dc, "\tthumb: 0x%04x\n" "\tthumb from %d to %d", orient == wxVERTICAL ? "vertical" : "horizontal", - flags[wxHT_SCROLLBAR_ARROW_LINE_1], - flags[wxHT_SCROLLBAR_ARROW_LINE_2], - flags[wxHT_SCROLLBAR_THUMB], + flags[wxScrollBar::Element_Arrow_Line_1], + flags[wxScrollBar::Element_Arrow_Line_2], + flags[wxScrollBar::Element_Thumb], thumbPosStart, thumbPosEnd); #endif // DEBUG_MOUSE @@ -813,7 +815,7 @@ void wxGTKRenderer::DrawScrollbar(wxDC& dc, for ( size_t nArrow = 0; nArrow < 2; nArrow++ ) { DrawArrow(dc, arrowDir[nArrow], rectArrow[nArrow], - flags[wxHT_SCROLLBAR_ARROW_LINE_1 + nArrow]); + flags[wxScrollBar::Element_Arrow_Line_1 + nArrow]); } // and, finally, the thumb, if any @@ -837,19 +839,30 @@ void wxGTKRenderer::DrawScrollbar(wxDC& dc, // the thumb is never pressed never has focus border under GTK and the // scrollbar background never changes at all - int flagsThumb = flags[wxHT_SCROLLBAR_THUMB] & + int flagsThumb = flags[wxScrollBar::Element_Thumb] & ~(wxCONTROL_PRESSED | wxCONTROL_FOCUSED); DrawButtonBorder(dc, rectThumb, flagsThumb, &rectThumb); DrawBackground(dc, rectThumb, flagsThumb); } } -wxHitTest wxGTKRenderer::HitTestScrollbar(wxScrollBar *scrollbar, +wxHitTest wxGTKRenderer::HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const { return StandardHitTestScrollbar(scrollbar, pt, m_sizeScrollbarArrow); } +wxCoord wxGTKRenderer::ScrollbarToPixel(const wxScrollBar *scrollbar) +{ + return StandardScrollbarToPixel(scrollbar, m_sizeScrollbarArrow); +} + +int wxGTKRenderer::PixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord) +{ + return StandardPixelToScrollbar(scrollbar, coord, m_sizeScrollbarArrow); +} + // ---------------------------------------------------------------------------- // size adjustments // ---------------------------------------------------------------------------- diff --git a/src/univ/themes/win32.cpp b/src/univ/themes/win32.cpp index 178a35269a..951c21c694 100644 --- a/src/univ/themes/win32.cpp +++ b/src/univ/themes/win32.cpp @@ -106,8 +106,10 @@ public: virtual void AdjustSize(wxSize *size, const wxWindow *window); - virtual wxHitTest HitTestScrollbar(wxScrollBar *scrollbar, + virtual wxHitTest HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const; + virtual wxCoord ScrollbarToPixel(const wxScrollBar *scrollbar); + virtual int PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord); protected: // DrawButtonBorder() helper @@ -1049,12 +1051,23 @@ void wxWin32Renderer::DrawScrollbar(wxDC& dc, } } -wxHitTest wxWin32Renderer::HitTestScrollbar(wxScrollBar *scrollbar, +wxHitTest wxWin32Renderer::HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const { return StandardHitTestScrollbar(scrollbar, pt, m_sizeScrollbarArrow); } +wxCoord wxWin32Renderer::ScrollbarToPixel(const wxScrollBar *scrollbar) +{ + return StandardScrollbarToPixel(scrollbar, m_sizeScrollbarArrow); +} + +int wxWin32Renderer::PixelToScrollbar(const wxScrollBar *scrollbar, + wxCoord coord) +{ + return StandardPixelToScrollbar(scrollbar, coord, m_sizeScrollbarArrow); +} + // ---------------------------------------------------------------------------- // size adjustments // ----------------------------------------------------------------------------