From 89196a1dabe6562c273ce097b2a9e8f794c5d27c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 24 Aug 2000 12:12:35 +0000 Subject: [PATCH] more scrollbar input handling: extracted common bits to wxStdScrollBarHandler git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/wxUNIVERSAL@8174 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/univ/inphand.h | 111 ++++++++++++ include/wx/univ/renderer.h | 2 + include/wx/univ/scrolbar.h | 18 +- src/msw/window.cpp | 21 ++- src/univ/inphand.cpp | 309 +++++++++++++++++++++++++++++++ src/univ/scrolbar.cpp | 2 +- src/univ/themes/gtk.cpp | 362 ++----------------------------------- src/univ/themes/win32.cpp | 328 +++++++++++---------------------- 8 files changed, 570 insertions(+), 583 deletions(-) diff --git a/include/wx/univ/inphand.h b/include/wx/univ/inphand.h index 37f2831a60..c12ff14273 100644 --- a/include/wx/univ/inphand.h +++ b/include/wx/univ/inphand.h @@ -50,4 +50,115 @@ public: virtual ~wxInputHandler(); }; +// ============================================================================ +// The classes below provide the standard input handling for some controls so +// that the code can be reused in different themes. All of these classes chain +// to an existing input handler (which must be given to the ctor) and so allow +// custom processing of the events which they don't handle themselves. +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxStdInputHandler is just a base class for all other "standard" handlers +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxStdInputHandler : public wxInputHandler +{ +public: + wxStdInputHandler(wxInputHandler *handler) : m_handler(handler) { } + + virtual wxControlActions Map(wxControl *control, + const wxKeyEvent& event, + bool pressed) + { return m_handler->Map(control, event, pressed); } + virtual wxControlActions Map(wxControl *control, + const wxMouseEvent& event) + { return m_handler->Map(control, event); } + virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event) + { return m_handler->OnMouseMove(control, event); } + +private: + wxInputHandler *m_handler; +}; + +// ---------------------------------------------------------------------------- +// wxStdButtonInputHandler: translates SPACE and ENTER keys and the left mouse +// click into button press/release actions +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxStdButtonInputHandler : public wxStdInputHandler +{ +public: + wxStdButtonInputHandler(wxInputHandler *inphand); + + virtual wxControlActions Map(wxControl *control, + const wxKeyEvent& event, + bool pressed); + virtual wxControlActions Map(wxControl *control, + const wxMouseEvent& event); + virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event); + +private: + // the window (button) which has capture or NULL and the flag telling if + // the mouse is inside the button which captured it or not + wxWindow *m_winCapture; + bool m_winHasMouse; +}; + +// ---------------------------------------------------------------------------- +// common scrollbar input handler: it manages clicks on the standard scrollbar +// elements (line arrows, bar, thumb) +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxStdScrollBarInputHandler : public wxStdInputHandler +{ +public: + // constructor takes a renderer (used for scrollbar hit testing) and the + // base handler to which all unhandled events are forwarded + wxStdScrollBarInputHandler(wxRenderer *renderer, + wxInputHandler *inphand); + + virtual wxControlActions Map(wxControl *control, + const wxKeyEvent& event, + bool pressed); + virtual wxControlActions Map(wxControl *control, + const wxMouseEvent& event); + virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event); + +protected: + // the methods which must be overridden in the derived class + + // return TRUE to stop scrolling when the mouse leaves the scrollbar (Win32 + // behaviour) or FALSE to continue (GTK+) + virtual bool OnMouseLeave() = 0; + + // return TRUE if the mouse button can be used to activate scrollbar, FALSE + // if not (only left mouse button can do it under Windows, any button under + // GTK+) + virtual bool IsAllowedButton(int button) = 0; + + // set or clear the specified flag on the scrollbar element corresponding + // to m_htLast + void SetElementState(wxScrollBar *scrollbar, int flag, bool doIt); + + // [un]highlight the scrollbar element corresponding to m_htLast + void Highlight(wxScrollBar *scrollbar, bool doIt) + { SetElementState(scrollbar, wxCONTROL_CURRENT, doIt); } + + // [un]press the scrollbar element corresponding to m_htLast + void Press(wxScrollBar *scrollbar, bool doIt) + { SetElementState(scrollbar, wxCONTROL_PRESSED, doIt); } + + // the window (scrollbar) which has capture or NULL and the flag telling if + // the mouse is inside the element which captured it or not + wxWindow *m_winCapture; + bool m_winHasMouse; + int m_btnCapture; // the mouse button which has captured mouse + + // one of wxHT_SCROLLBAR_XXX value: where has the mouse been last time? + wxHitTest m_htLast; + + // the renderer (we use it only for hit testing) + wxRenderer *m_renderer; +}; + #endif // _WX_UNIV_INPHAND_H_ diff --git a/include/wx/univ/renderer.h b/include/wx/univ/renderer.h index 7e3e5555bd..acbddcc249 100644 --- a/include/wx/univ/renderer.h +++ b/include/wx/univ/renderer.h @@ -46,6 +46,7 @@ enum wxHitTest wxHT_NOWHERE, // scrollbar + wxHT_SCROLLBAR_FIRST = wxHT_NOWHERE, wxHT_SCROLLBAR_ARROW_LINE_1, // left or upper arrow to scroll by line wxHT_SCROLLBAR_ARROW_LINE_2, // right or down wxHT_SCROLLBAR_ARROW_PAGE_1, // left or upper arrow to scroll by page @@ -53,6 +54,7 @@ enum wxHitTest wxHT_SCROLLBAR_THUMB, // on the thumb wxHT_SCROLLBAR_BAR_1, // bar to the left/above the thumb wxHT_SCROLLBAR_BAR_2, // bar to the right/below the thumb + wxHT_SCROLLBAR_LAST, wxHT_MAX }; diff --git a/include/wx/univ/scrolbar.h b/include/wx/univ/scrolbar.h index f4ff730eaa..b706d0dd1f 100644 --- a/include/wx/univ/scrolbar.h +++ b/include/wx/univ/scrolbar.h @@ -16,6 +16,8 @@ #pragma interface "univscrolbar.h" #endif +#include "wx/univ/renderer.h" // for wxHitTest + class WXDLLEXPORT wxInputHandler; // ---------------------------------------------------------------------------- @@ -42,15 +44,17 @@ class WXDLLEXPORT wxInputHandler; class WXDLLEXPORT wxScrollBar : public wxScrollBarBase { public: - // the parts of the scrollbar + // scrollbar elements: they correspond to wxHT_SCROLLBAR_XXX constants but + // start from 0 which allows to use them as array indices enum Element { Element_Arrow_Line_1, Element_Arrow_Line_2, Element_Arrow_Page_1, Element_Arrow_Page_2, - Element_Thumb, - Element_Bar, + Element_Arrow_Thumb, + Element_Bar_1, + Element_Bar_2, Element_Max }; @@ -99,10 +103,8 @@ public: const wxEvent& event); // wxScrollBar sub elements state (combination of wxCONTROL_XXX) - void SetState(Element elem, int flags) - { m_elementsState[elem] = flags; } - int GetState(Element elem) const - { return m_elementsState[elem]; } + void SetState(Element which, int flags) { m_elementsState[which] = flags; } + int GetState(Element which) const { return m_elementsState[which]; } protected: virtual wxSize DoGetBestSize() const; @@ -110,7 +112,7 @@ protected: // SetThumbPosition() helper void DoSetThumb(int thumbPos); - + // common part of all ctors void Init(); diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 0ee7511007..1b1b69184e 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -1015,7 +1015,7 @@ void wxWindow::OnIdle(wxIdleEvent& event) state |= MK_MBUTTON; if ( GetKeyState( VK_RBUTTON ) ) state |= MK_RBUTTON; - + wxMouseEvent event(wxEVT_LEAVE_WINDOW); InitMouseEvent(event, pt.x, pt.y, state); @@ -3273,13 +3273,22 @@ bool wxWindow::HandleMouseMove(int x, int y, WXUINT flags) { if ( !m_mouseInWindow ) { - // Generate an ENTER event - m_mouseInWindow = TRUE; + // it would be wrogn to assume that just because we get a mouse move + // event the mouse is inside the window: although this is usually true, + // it is not if we had captured the mouse, so we need to check the + // mouse coordinates here + POINT pt; + ::GetCursorPos(&pt); + if ( ::WindowFromPoint(pt) == GetHwnd() ) + { + // Generate an ENTER event + m_mouseInWindow = TRUE; - wxMouseEvent event(wxEVT_ENTER_WINDOW); - InitMouseEvent(event, x, y, flags); + wxMouseEvent event(wxEVT_ENTER_WINDOW); + InitMouseEvent(event, x, y, flags); - (void)GetEventHandler()->ProcessEvent(event); + (void)GetEventHandler()->ProcessEvent(event); + } } #if wxUSE_MOUSEEVENT_HACK diff --git a/src/univ/inphand.cpp b/src/univ/inphand.cpp index 1754fb9422..17a8ad6542 100644 --- a/src/univ/inphand.cpp +++ b/src/univ/inphand.cpp @@ -37,6 +37,10 @@ // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// wxInputHandler +// ---------------------------------------------------------------------------- + bool wxInputHandler::OnMouseMove(wxControl * WXUNUSED(control), const wxMouseEvent& WXUNUSED(event)) { @@ -46,3 +50,308 @@ bool wxInputHandler::OnMouseMove(wxControl * WXUNUSED(control), wxInputHandler::~wxInputHandler() { } + +// ---------------------------------------------------------------------------- +// wxStdButtonInputHandler +// ---------------------------------------------------------------------------- + +wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler) + : wxStdInputHandler(handler) +{ + m_winCapture = NULL; +} + +wxControlActions wxStdButtonInputHandler::Map(wxControl *control, + const wxKeyEvent& event, + bool pressed) +{ + int keycode = event.GetKeyCode(); + if ( keycode == WXK_SPACE || keycode == WXK_RETURN ) + { + return wxACTION_BUTTON_TOGGLE; + } + + return wxStdInputHandler::Map(control, event, pressed); +} + +wxControlActions wxStdButtonInputHandler::Map(wxControl *control, + const wxMouseEvent& event) +{ + // the button has 2 states: pressed and normal with the following + // transitions between them: + // + // normal -> left down -> capture mouse and go to pressed state + // pressed -> left up inside -> generate click -> go to normal + // outside ------------------> + // + // the other mouse buttons are ignored + if ( event.Button(1) ) + { + if ( event.ButtonDown(1) ) + { + m_winCapture = control; + m_winCapture->CaptureMouse(); + m_winHasMouse = TRUE; + + return wxACTION_BUTTON_PRESS; + } + else // up + { + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + + if ( m_winHasMouse ) + { + // this will generate a click event + return wxACTION_BUTTON_TOGGLE; + } + //else: the mouse was released outside the window, this doesn't + // count as a click + } + } + + return wxStdInputHandler::Map(control, event); +} + +bool wxStdButtonInputHandler::OnMouseMove(wxControl *control, + const wxMouseEvent& event) +{ + // we only have to do soemthing when the mouse leaves/enters the pressed + // button and don't care about the other ones + if ( event.GetEventObject() == m_winCapture ) + { + // leaving the button should remove its pressed state + if ( event.Leaving() ) + { + // remember that the mouse is now outside + m_winHasMouse = FALSE; + + // we do have a pressed button, so release it + if ( control->PerformAction(wxACTION_BUTTON_RELEASE, event) ) + { + // the button state changed, refresh needed + return TRUE; + } + } + // and entering it back should make it pressed again if it had been + // pressed + else if ( event.Entering() ) + { + // the mouse is (back) inside the button + m_winHasMouse = TRUE; + + // we did have a pressed button which we released when leaving the + // window, press it again + if ( control->PerformAction(wxACTION_BUTTON_PRESS, event) ) + { + return TRUE; + } + } + } + + return wxStdInputHandler::OnMouseMove(control, event); +} + +// ---------------------------------------------------------------------------- +// wxStdScrollBarInputHandler +// ---------------------------------------------------------------------------- + +wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer, + wxInputHandler *handler) + : wxStdInputHandler(handler) +{ + m_renderer = renderer; + m_winCapture = NULL; + m_htLast = wxHT_NOWHERE; +} + +void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control, + int flag, + bool doIt) +{ + if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST ) + { + wxScrollBar::Element + elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1); + + int flags = control->GetState(elem); + if ( doIt ) + flags |= flag; + else + flags &= ~flag; + control->SetState(elem, flags); + } +} + +wxControlActions wxStdScrollBarInputHandler::Map(wxControl *control, + const wxKeyEvent& event, + bool pressed) +{ + // we only react to the key presses here + if ( pressed ) + { + switch ( event.GetKeyCode() ) + { + case WXK_DOWN: + case WXK_RIGHT: return wxACTION_SCROLL_LINE_DOWN; + case WXK_UP: + case WXK_LEFT: return wxACTION_SCROLL_LINE_UP; + case WXK_HOME: return wxACTION_SCROLL_START; + case WXK_END: return wxACTION_SCROLL_END; + case WXK_PRIOR: return wxACTION_SCROLL_PAGE_UP; + case WXK_NEXT: return wxACTION_SCROLL_PAGE_DOWN; + } + } + + return wxStdInputHandler::Map(control, event, pressed); +} + +wxControlActions wxStdScrollBarInputHandler::Map(wxControl *control, + const wxMouseEvent& event) +{ + // is this a click event from an acceptable button? + int btn = -1; + if ( event.IsButton() ) + { + for ( int i = 1; i <= 3; i++ ) + { + if ( event.Button(i) ) + { + btn = i; + break; + } + } + + wxASSERT_MSG( btn != -1, _T("unknown mouse button") ); + } + + if ( (btn != -1) && IsAllowedButton(btn) ) + { + // determine which part of the window mouse is in + wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar); + wxHitTest ht = m_renderer->HitTestScrollbar + ( + scrollbar, + event.GetPosition() + ); + + // when the mouse is pressed on any scrollbar element, we capture it + // and hold capture until the same mouse button is released + if ( event.ButtonDown() ) + { + if ( !m_winCapture ) + { + m_btnCapture = btn; + m_winCapture = control; + m_winCapture->CaptureMouse(); + + // generate the command + bool hasAction = TRUE; + wxControlAction action; + switch ( ht ) + { + case wxHT_SCROLLBAR_ARROW_LINE_1: + action = wxACTION_SCROLL_LINE_UP; + break; + + case wxHT_SCROLLBAR_ARROW_LINE_2: + action = wxACTION_SCROLL_LINE_DOWN; + break; + + case wxHT_SCROLLBAR_BAR_1: + action = wxACTION_SCROLL_PAGE_UP; + break; + + case wxHT_SCROLLBAR_BAR_2: + action = wxACTION_SCROLL_PAGE_DOWN; + break; + + default: + hasAction = FALSE; + } + + if ( hasAction ) + { + control->PerformAction(action, event); + } + + // remove highlighting and press the arrow instead + Highlight(scrollbar, FALSE); + m_htLast = ht; + Press(scrollbar, TRUE); + + control->Refresh(); + } + //else: mouse already captured, nothing to do + } + // release mouse if the *same* button went up + else if ( btn == m_btnCapture ) + { + if ( m_winCapture ) + { + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + m_btnCapture = -1; + + // unpress the arrow and highlight the current element + Press(scrollbar, FALSE); + m_htLast = ht; + Highlight(scrollbar, TRUE); + + control->Refresh(); + } + else + { + // this is not supposed to happen as the button can't go up + // without going down previously and then we'd have + // m_winCapture by now + wxFAIL_MSG( _T("logic error in mouse capturing code") ); + } + } + } + + return wxStdInputHandler::Map(control, event); +} + +bool wxStdScrollBarInputHandler::OnMouseMove(wxControl *control, + const wxMouseEvent& event) +{ + if ( m_winCapture ) + { + // everything is locked while the mouse is captured, so don't do + // anything + return FALSE; + } + + wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar); + + if ( event.Moving() ) + { + wxHitTest ht = m_renderer->HitTestScrollbar + ( + scrollbar, + event.GetPosition() + ); + if ( ht == m_htLast ) + { + // nothing changed + return FALSE; + } + +#ifdef DEBUG_MOUSE + wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht); +#endif // DEBUG_MOUSE + + Highlight(scrollbar, FALSE); + m_htLast = ht; + Highlight(scrollbar, TRUE); + } + else if ( event.Leaving() ) + { + Highlight(scrollbar, FALSE); + m_htLast = wxHT_NOWHERE; + } + + // highlighting changed + return TRUE; +} diff --git a/src/univ/scrolbar.cpp b/src/univ/scrolbar.cpp index 096badef11..056f76d146 100644 --- a/src/univ/scrolbar.cpp +++ b/src/univ/scrolbar.cpp @@ -56,7 +56,7 @@ void wxScrollBar::Init() m_thumbPos = m_pageSize = 0; - for ( size_t n = 0; n < Element_Max; n++ ) + for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ ) { m_elementsState[n] = 0; } diff --git a/src/univ/themes/gtk.cpp b/src/univ/themes/gtk.cpp index c88ff1437b..8f883308ce 100644 --- a/src/univ/themes/gtk.cpp +++ b/src/univ/themes/gtk.cpp @@ -162,53 +162,15 @@ protected: wxGTKRenderer *m_renderer; }; -class wxGTKButtonInputHandler : public wxGTKInputHandler +class wxGTKScrollBarInputHandler : public wxStdScrollBarInputHandler { public: - wxGTKButtonInputHandler(wxGTKRenderer *renderer); + wxGTKScrollBarInputHandler(wxRenderer *renderer, wxInputHandler *handler) + : wxStdScrollBarInputHandler(renderer, handler) { } - virtual wxControlActions Map(wxControl *control, - const wxKeyEvent& event, - bool pressed); - virtual wxControlActions Map(wxControl *control, - const wxMouseEvent& event); - virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event); - -private: - wxWindow *m_winCapture; - bool m_winHasMouse; -}; - -class wxGTKScrollBarInputHandler : public wxGTKInputHandler -{ -public: - wxGTKScrollBarInputHandler(wxGTKRenderer *renderer); - - virtual wxControlActions Map(wxControl *control, - const wxKeyEvent& event, - bool pressed); - virtual wxControlActions Map(wxControl *control, - const wxMouseEvent& event); - virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event); - -private: - // set or clear the specified flag on the scrollbar element corresponding - // to m_htLast - void SetElementState(wxScrollBar *scrollbar, int flag, bool doIt); - - // [un]highlight the scrollbar element corresponding to m_htLast - void Highlight(wxScrollBar *scrollbar, bool doIt) - { SetElementState(scrollbar, wxCONTROL_CURRENT, doIt); } - - // [un]press the scrollbar element corresponding to m_htLast - void Press(wxScrollBar *scrollbar, bool doIt) - { SetElementState(scrollbar, wxCONTROL_PRESSED, doIt); } - - wxWindow *m_winCapture; - int m_btnCapture; // the mouse button which was captured mouse - bool m_winHasMouse; - - wxHitTest m_htLast; +protected: + virtual bool OnMouseLeave() { return FALSE; } + virtual bool IsAllowedButton(int WXUNUSED(button)) { return TRUE; } }; // ---------------------------------------------------------------------------- @@ -282,9 +244,10 @@ wxInputHandler *wxGTKTheme::GetInputHandler(const wxString& control) n = m_handlerNames.Add(control); if ( control == _T("wxButton") ) - handler = new wxGTKButtonInputHandler(m_renderer); + handler = new wxStdButtonInputHandler(GetInputHandler(_T("wxControl"))); else if ( control == _T("wxScrollBar") ) - handler = new wxGTKScrollBarInputHandler(m_renderer); + handler = new wxGTKScrollBarInputHandler(m_renderer, + GetInputHandler(_T("wxControl"))); else handler = new wxGTKInputHandler(m_renderer); @@ -810,9 +773,9 @@ void wxGTKRenderer::DrawScrollbar(wxDC& dc, "\tthumb: 0x%04x\n" "\tthumb from %d to %d", orient == wxVERTICAL ? "vertical" : "horizontal", - flags[wxScrollBar::Element_Arrow_Line_1], - flags[wxScrollBar::Element_Arrow_Line_2], - flags[wxScrollBar::Element_Thumb], + flags[wxHT_SCROLLBAR_ARROW_LINE_1], + flags[wxHT_SCROLLBAR_ARROW_LINE_2], + flags[wxHT_SCROLLBAR_THUMB], thumbPosStart, thumbPosEnd); #endif // DEBUG_MOUSE @@ -850,7 +813,7 @@ void wxGTKRenderer::DrawScrollbar(wxDC& dc, for ( size_t nArrow = 0; nArrow < 2; nArrow++ ) { DrawArrow(dc, arrowDir[nArrow], rectArrow[nArrow], - flags[wxScrollBar::Element_Arrow_Line_1 + nArrow]); + flags[wxHT_SCROLLBAR_ARROW_LINE_1 + nArrow]); } // and, finally, the thumb, if any @@ -874,7 +837,7 @@ 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[wxScrollBar::Element_Thumb] & + int flagsThumb = flags[wxHT_SCROLLBAR_THUMB] & ~(wxCONTROL_PRESSED | wxCONTROL_FOCUSED); DrawButtonBorder(dc, rectThumb, flagsThumb, &rectThumb); DrawBackground(dc, rectThumb, flagsThumb); @@ -995,300 +958,3 @@ bool wxGTKInputHandler::OnMouseMove(wxControl *control, return TRUE; } -// ---------------------------------------------------------------------------- -// wxGTKButtonInputHandler -// ---------------------------------------------------------------------------- - -wxGTKButtonInputHandler::wxGTKButtonInputHandler(wxGTKRenderer *renderer) - : wxGTKInputHandler(renderer) -{ - m_winCapture = NULL; -} - -wxControlActions wxGTKButtonInputHandler::Map(wxControl *control, - const wxKeyEvent& event, - bool pressed) -{ - int keycode = event.GetKeyCode(); - if ( keycode == WXK_SPACE || keycode == WXK_RETURN ) - { - return wxACTION_BUTTON_TOGGLE; - } - - return wxGTKInputHandler::Map(control, event, pressed); -} - -wxControlActions wxGTKButtonInputHandler::Map(wxControl *control, - const wxMouseEvent& event) -{ - // the button has 2 states: pressed and normal with the following - // transitions between them: - // - // normal -> left down -> capture mouse and go to pressed state - // pressed -> left up inside -> generate click -> go to normal - // outside ------------------> - // - // the other mouse buttons are ignored - if ( event.Button(1) ) - { - if ( event.ButtonDown(1) ) - { - m_winCapture = control; - m_winCapture->CaptureMouse(); - m_winHasMouse = TRUE; - - return wxACTION_BUTTON_PRESS; - } - else // up - { - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - - if ( m_winHasMouse ) - { - // this will generate a click event - return wxACTION_BUTTON_TOGGLE; - } - //else: the mouse was released outside the window, this doesn't - // count as a click - } - } - - return wxGTKInputHandler::Map(control, event); -} - -bool wxGTKButtonInputHandler::OnMouseMove(wxControl *control, - const wxMouseEvent& event) -{ - // leaving the button should remove its pressed state - if ( event.Leaving() ) - { - if ( m_winCapture ) - { - // we do have a pressed button, so release it - control->PerformAction(wxACTION_BUTTON_RELEASE, event); - - // remember that the mouse is now outside - m_winHasMouse = FALSE; - } - } - // and entering it back should make it pressed again if it had been - // pressed - else if ( event.Entering() ) - { - if ( m_winCapture ) - { - // we did have a pressed button which we released when leaving the - // window, press it again - control->PerformAction(wxACTION_BUTTON_PRESS, event); - - // and the mouse is (back) inside it - m_winHasMouse = TRUE; - } - } - - return wxGTKInputHandler::OnMouseMove(control, event); -} - -// ---------------------------------------------------------------------------- -// wxGTKScrollBarInputHandler -// ---------------------------------------------------------------------------- - -wxGTKScrollBarInputHandler::wxGTKScrollBarInputHandler(wxGTKRenderer *renderer) - : wxGTKInputHandler(renderer) -{ - m_winCapture = NULL; - m_htLast = wxHT_NOWHERE; -} - -void wxGTKScrollBarInputHandler::SetElementState(wxScrollBar *control, - int flag, - bool doIt) -{ - wxScrollBar::Element elem; - switch ( m_htLast ) - { - case wxHT_SCROLLBAR_ARROW_LINE_1: - elem = wxScrollBar::Element_Arrow_Line_1; - break; - - case wxHT_SCROLLBAR_ARROW_LINE_2: - elem = wxScrollBar::Element_Arrow_Line_2; - break; - - case wxHT_SCROLLBAR_THUMB: - elem = wxScrollBar::Element_Thumb; - break; - - /* - we don't highlight nor press the bar - - case wxHT_SCROLLBAR_BAR_1: - case wxHT_SCROLLBAR_BAR_2: - */ - - default: - elem = wxScrollBar::Element_Max; - } - - if ( elem != wxScrollBar::Element_Max ) - { - int flags = control->GetState(elem); - if ( doIt ) - flags |= flag; - else - flags &= ~flag; - control->SetState(elem, flags); - } -} - -wxControlActions wxGTKScrollBarInputHandler::Map(wxControl *control, - const wxKeyEvent& event, - bool pressed) -{ - // weirdly enough, GTK+ scrollbars don't have keyboard support - maybe we - // should still have it though (TODO)? - return wxGTKInputHandler::Map(control, event, pressed); -} - -wxControlActions wxGTKScrollBarInputHandler::Map(wxControl *control, - const wxMouseEvent& event) -{ - if ( event.IsButton() ) - { - // determine which part of the window mouse is in - wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar); - wxHitTest ht = m_renderer->HitTestScrollbar - ( - scrollbar, - event.GetPosition() - ); - - // when the mouse is pressed on any scrollbar element, we capture it - // and hold capture until the same mouse button is released - if ( event.ButtonDown() ) - { - if ( !m_winCapture ) - { - m_btnCapture = -1; - for ( int i = 1; i <= 3; i++ ) - { - if ( event.ButtonDown(i) ) - { - m_btnCapture = i; - break; - } - } - - wxASSERT_MSG( m_btnCapture != -1, _T("unknown mouse button") ); - - m_winCapture = control; - m_winCapture->CaptureMouse(); - - // generate the command - bool hasAction = TRUE; - wxControlAction action; - switch ( ht ) - { - case wxHT_SCROLLBAR_ARROW_LINE_1: - action = wxACTION_SCROLL_LINE_UP; - break; - - case wxHT_SCROLLBAR_ARROW_LINE_2: - action = wxACTION_SCROLL_LINE_DOWN; - break; - - case wxHT_SCROLLBAR_BAR_1: - action = wxACTION_SCROLL_PAGE_UP; - break; - - case wxHT_SCROLLBAR_BAR_2: - action = wxACTION_SCROLL_PAGE_DOWN; - break; - - default: - hasAction = FALSE; - } - - if ( hasAction ) - { - control->PerformAction(action, event); - } - - // remove highlighting and press the arrow instead - Highlight(scrollbar, FALSE); - m_htLast = ht; - Press(scrollbar, TRUE); - } - //else: mouse already captured, nothing to do - } - // release mouse if the *same* button went up - else if ( event.ButtonUp(m_btnCapture) ) - { - if ( m_winCapture ) - { - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - - // unpress the arrow and highlight the current element - Press(scrollbar, TRUE); - m_htLast = ht; - Highlight(scrollbar, TRUE); - - control->Refresh(); - } - else - { - // this is not supposed to happen as the button can't go up - // without going down previously and then we'd have - // m_winCapture by now - wxFAIL_MSG( _T("logic error in mouse capturing code") ); - } - } - } - - return wxGTKInputHandler::Map(control, event); -} - -bool wxGTKScrollBarInputHandler::OnMouseMove(wxControl *control, - const wxMouseEvent& event) -{ - if ( m_winCapture ) - { - // everything is locked while the mouse is captured, so don't do - // anything - return FALSE; - } - - wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar); - - if ( event.Moving() ) - { - wxHitTest ht = m_renderer->HitTestScrollbar - ( - scrollbar, - event.GetPosition() - ); - if ( ht == m_htLast ) - { - // nothing changed - return FALSE; - } - -#ifdef DEBUG_MOUSE - wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht); -#endif // DEBUG_MOUSE - - Highlight(scrollbar, FALSE); - m_htLast = ht; - Highlight(scrollbar, TRUE); - } - else if ( event.Leaving() ) - { - Highlight(scrollbar, FALSE); - m_htLast = wxHT_NOWHERE; - } - - // highlighting changed - return TRUE; -} diff --git a/src/univ/themes/win32.cpp b/src/univ/themes/win32.cpp index a0cc82922c..178a35269a 100644 --- a/src/univ/themes/win32.cpp +++ b/src/univ/themes/win32.cpp @@ -61,6 +61,7 @@ public: { Arrow_Normal, Arrow_Disabled, + Arrow_Pressed, Arrow_StateMax }; @@ -132,7 +133,7 @@ protected: void DrawRaisedBorder(wxDC& dc, wxRect *rect); // draw the border used for scrollbar arrows - void DrawArrowBorder(wxDC& dc, wxRect *rect); + void DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed = FALSE); // public DrawArrow()s helper void DrawArrow(wxDC& dc, const wxRect& rect, @@ -182,38 +183,26 @@ protected: wxWin32Renderer *m_renderer; }; -class wxWin32ButtonInputHandler : public wxWin32InputHandler +class wxWin32ScrollBarInputHandler : public wxStdScrollBarInputHandler { public: - wxWin32ButtonInputHandler(wxWin32Renderer *renderer); + wxWin32ScrollBarInputHandler(wxWin32Renderer *renderer, + wxInputHandler *handler) + : wxStdScrollBarInputHandler(renderer, handler) { } - virtual wxControlActions Map(wxControl *control, - const wxKeyEvent& event, - bool pressed); - virtual wxControlActions Map(wxControl *control, - const wxMouseEvent& event); + // we don't highlight scrollbar elements, so there is no need to process + // mouse move events + virtual bool OnMouseMove(wxControl *control, const wxMouseEvent& event) + { + if ( event.Moving() ) + return FALSE; -private: - wxWindow *m_winCapture; -}; + return wxStdScrollBarInputHandler::OnMouseMove(control, event); + } -class wxWin32ScrollBarInputHandler : public wxWin32InputHandler -{ -public: - wxWin32ScrollBarInputHandler(wxWin32Renderer *renderer); - - virtual wxControlActions Map(wxControl *control, - const wxKeyEvent& event, - bool pressed); - virtual wxControlActions Map(wxControl *control, - const wxMouseEvent& event); - -private: - void Press(wxScrollBar *scrollbar, bool doIt) { } - - wxWindow *m_winCapture; - int m_btnCapture; - wxHitTest m_htLast; +protected: + virtual bool OnMouseLeave() { return TRUE; } + virtual bool IsAllowedButton(int button) { return button == 1; } }; // ---------------------------------------------------------------------------- @@ -289,9 +278,10 @@ wxInputHandler *wxWin32Theme::GetInputHandler(const wxString& control) n = m_handlerNames.Add(control); if ( control == _T("wxButton") ) - handler = new wxWin32ButtonInputHandler(m_renderer); + handler = new wxStdButtonInputHandler(GetInputHandler(_T("wxControl"))); else if ( control == _T("wxScrollBar") ) - handler = new wxWin32ScrollBarInputHandler(m_renderer); + handler = new wxWin32ScrollBarInputHandler(m_renderer, + GetInputHandler(_T("wxControl"))); else handler = new wxWin32InputHandler(m_renderer); @@ -322,6 +312,10 @@ wxColour wxWin32ColourScheme::Get(wxWin32ColourScheme::StdColour col, { case CONTROL: return wxColour(0xc0c0c0); case CONTROL_TEXT: return *wxBLACK; + case SCROLLBAR: if ( flags & wxCONTROL_PRESSED ) + return *wxBLACK; + else + return wxColour(0xe0e0e0); case HIGHLIGHT: return wxColour(0x800000); case HIGHLIGHT_TEXT: return wxColour(0xffffff); @@ -500,6 +494,8 @@ wxWin32Renderer::wxWin32Renderer(const wxColourScheme *scheme) m_bmpArrows[Arrow_Normal][n].SetMask(mask); mask = new wxMask(m_bmpArrows[Arrow_Disabled][n], *wxWHITE); m_bmpArrows[Arrow_Disabled][n].SetMask(mask); + + m_bmpArrows[Arrow_Pressed][n] = m_bmpArrows[Arrow_Normal][n]; } } @@ -624,10 +620,25 @@ void wxWin32Renderer::DrawRaisedBorder(wxDC& dc, wxRect *rect) DrawShadedRect(dc, rect, m_penLightGrey, m_penDarkGrey); } -void wxWin32Renderer::DrawArrowBorder(wxDC& dc, wxRect *rect) +void wxWin32Renderer::DrawArrowBorder(wxDC& dc, wxRect *rect, bool isPressed) { - DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack); - DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey); + if ( isPressed ) + { + DrawRect(dc, rect, m_penDarkGrey); + + // the arrow is usually drawn inside border of width 2 and is offset by + // another pixel in both directions when it's pressed - as the border + // in this case is more narrow as well, we have to adjust rect like + // this: + rect->Inflate(-1); + rect->x++; + rect->y++; + } + else + { + DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack); + DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey); + } } void wxWin32Renderer::DrawBorder(wxDC& dc, @@ -912,7 +923,7 @@ void wxWin32Renderer::DrawArrowButton(wxDC& dc, wxArrowStyle arrowStyle) { wxRect rect = rectAll; - DrawArrowBorder(dc, &rect); + DrawArrowBorder(dc, &rect, arrowStyle == Arrow_Pressed); DrawArrow(dc, rect, arrowDir, arrowStyle); } @@ -923,10 +934,6 @@ void wxWin32Renderer::DrawScrollbar(wxDC& dc, const wxRect& rect, const int *flags) { - int flagsSb = flags ? flags[0] : 0; - wxArrowStyle arrowStyle = flagsSb & wxCONTROL_DISABLED ? Arrow_Disabled - : Arrow_Normal; - // first, draw the arrows at the ends wxRect rectArrow[2]; wxArrowDirection arrowDir[2]; @@ -952,24 +959,34 @@ void wxWin32Renderer::DrawScrollbar(wxDC& dc, arrowDir[1] = Arrow_Right; } + wxArrowStyle arrowStyle; for ( size_t nArrow = 0; nArrow < 2; nArrow++ ) { + int flagsArrow = flags[wxScrollBar::Element_Arrow_Line_1 + nArrow]; + if ( flagsArrow & wxCONTROL_PRESSED ) + arrowStyle = Arrow_Pressed; + else if ( flagsArrow & wxCONTROL_DISABLED ) + arrowStyle = Arrow_Disabled; + else + arrowStyle = Arrow_Normal; + DrawArrowButton(dc, rectArrow[nArrow], arrowDir[nArrow], arrowStyle); } - // next draw the scrollbar area + // next draw the scrollbar area: in a normal state, we draw it all in one + // call to DoDrawBackground(), but when either part of the bar is pressed, + // we paint them separately wxRect rectBar = rect; if ( orient == wxVERTICAL ) - rectBar.Inflate(0, -m_sizeScrollbarArrow.y); + rectBar.Inflate(0, -(m_sizeScrollbarArrow.y + 1)); else - rectBar.Inflate(-m_sizeScrollbarArrow.x, 0); + rectBar.Inflate(-(m_sizeScrollbarArrow.x + 1), 0); - DoDrawBackground(dc, m_colHighlight, rectBar); - - // and, finally, the thumb, if any + // calculate the thumb position + wxRect rectThumb; if ( thumbPosStart < thumbPosEnd ) { - wxRect rectThumb = rectBar; + rectThumb = rectBar; if ( orient == wxVERTICAL ) { rectThumb.y += (rectBar.height*thumbPosStart)/100; @@ -980,7 +997,53 @@ void wxWin32Renderer::DrawScrollbar(wxDC& dc, rectThumb.x += (rectBar.width*thumbPosStart)/100; rectThumb.width = (rectBar.width*(thumbPosEnd - thumbPosStart))/100; } + } + //else: no thumb + if ( (flags[wxScrollBar::Element_Bar_1] & wxCONTROL_PRESSED) || + (flags[wxScrollBar::Element_Bar_2] & wxCONTROL_PRESSED) ) + { + // calculate the bounding boxes for each of 2 bar parts + wxRect rectBars[2]; + rectBars[0] = + rectBars[1] = rectBar; + if ( orient == wxVERTICAL ) + { + rectBars[0].SetTop(m_sizeScrollbarArrow.y); + rectBars[0].SetBottom(rectThumb.GetTop() - 1); + rectBars[1].SetTop(rectThumb.GetBottom() + 1); + rectBars[1].SetBottom(rectBar.GetBottom()); + } + else // horizontal + { + rectBars[0].SetLeft(m_sizeScrollbarArrow.x); + rectBars[0].SetRight(rectThumb.GetLeft() - 1); + rectBars[1].SetLeft(rectThumb.GetRight() + 1); + rectBars[1].SetRight(rectBar.GetRight()); + } + + for ( size_t nBar = 0; nBar < 2; nBar++ ) + { + DoDrawBackground( + dc, + m_scheme->Get + ( + wxColourScheme::SCROLLBAR, + flags[wxScrollBar::Element_Bar_1 + nBar] + ), + rectBars[nBar] + ); + } + } + else // nothing is pressed + { + DoDrawBackground(dc, m_scheme->Get(wxColourScheme::SCROLLBAR), rectBar); + } + + // and, finally, the thumb, if any + if ( thumbPosStart < thumbPosEnd ) + { + // we don't use the flags, the thumb never changes appearance DrawArrowBorder(dc, &rectThumb); DrawBackground(dc, rectThumb); } @@ -1072,178 +1135,3 @@ wxControlActions wxWin32InputHandler::Map(wxControl *control, { return wxACTION_NONE; } - -// ---------------------------------------------------------------------------- -// wxWin32ButtonInputHandler -// ---------------------------------------------------------------------------- - -wxWin32ButtonInputHandler::wxWin32ButtonInputHandler(wxWin32Renderer *renderer) - : wxWin32InputHandler(renderer) -{ - m_winCapture = NULL; -} - -wxControlActions wxWin32ButtonInputHandler::Map(wxControl *control, - const wxKeyEvent& event, - bool pressed) -{ - int keycode = event.GetKeyCode(); - if ( keycode == WXK_SPACE || keycode == WXK_RETURN ) - { - return wxACTION_BUTTON_TOGGLE; - } - - return wxWin32InputHandler::Map(control, event, pressed); -} - -wxControlActions wxWin32ButtonInputHandler::Map(wxControl *control, - const wxMouseEvent& event) -{ - if ( event.IsButton() ) - { - if ( event.ButtonDown() ) - { - m_winCapture = wxWindow::FindFocus(); - m_winCapture->CaptureMouse(); - } - else // up - { - m_winCapture->ReleaseMouse(); - } - - return wxACTION_BUTTON_TOGGLE; - } - - return wxWin32InputHandler::Map(control, event); -} - -// ---------------------------------------------------------------------------- -// wxWin32ScrollBarInputHandler -// ---------------------------------------------------------------------------- - -wxWin32ScrollBarInputHandler::wxWin32ScrollBarInputHandler(wxWin32Renderer *renderer) - : wxWin32InputHandler(renderer) -{ - m_winCapture = NULL; - m_htLast = wxHT_NOWHERE; -} - -wxControlActions wxWin32ScrollBarInputHandler::Map(wxControl *control, - const wxKeyEvent& event, - bool pressed) -{ - // we only react to the key presses here - if ( pressed ) - { - switch ( event.GetKeyCode() ) - { - case WXK_DOWN: - case WXK_RIGHT: return wxACTION_SCROLL_LINE_DOWN; - case WXK_UP: - case WXK_LEFT: return wxACTION_SCROLL_LINE_UP; - case WXK_HOME: return wxACTION_SCROLL_START; - case WXK_END: return wxACTION_SCROLL_END; - case WXK_PRIOR: return wxACTION_SCROLL_PAGE_UP; - case WXK_NEXT: return wxACTION_SCROLL_PAGE_DOWN; - } - } - - return wxWin32InputHandler::Map(control, event, pressed); -} - -wxControlActions wxWin32ScrollBarInputHandler::Map(wxControl *control, - const wxMouseEvent& event) -{ - if ( event.IsButton() ) - { - // determine which part of the window mouse is in - wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar); - wxHitTest ht = m_renderer->HitTestScrollbar - ( - scrollbar, - event.GetPosition() - ); - - // when the mouse is pressed on any scrollbar element, we capture it - // and hold capture until the same mouse button is released - if ( event.ButtonDown() ) - { - if ( !m_winCapture ) - { - m_btnCapture = -1; - for ( int i = 1; i <= 3; i++ ) - { - if ( event.ButtonDown(i) ) - { - m_btnCapture = i; - break; - } - } - - wxASSERT_MSG( m_btnCapture != -1, _T("unknown mouse button") ); - - m_winCapture = control; - m_winCapture->CaptureMouse(); - - // generate the command - bool hasAction = TRUE; - wxControlAction action; - switch ( ht ) - { - case wxHT_SCROLLBAR_ARROW_LINE_1: - action = wxACTION_SCROLL_LINE_UP; - break; - - case wxHT_SCROLLBAR_ARROW_LINE_2: - action = wxACTION_SCROLL_LINE_DOWN; - break; - - case wxHT_SCROLLBAR_BAR_1: - action = wxACTION_SCROLL_PAGE_UP; - break; - - case wxHT_SCROLLBAR_BAR_2: - action = wxACTION_SCROLL_PAGE_DOWN; - break; - - default: - hasAction = FALSE; - } - - if ( hasAction ) - { - control->PerformAction(action, event); - } - - // remove highlighting and press the arrow instead - m_htLast = ht; - Press(scrollbar, TRUE); - } - //else: mouse already captured, nothing to do - } - // release mouse if the *same* button went up - else if ( event.ButtonUp(m_btnCapture) ) - { - if ( m_winCapture ) - { - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - - // unpress the arrow and highlight the current element - Press(scrollbar, TRUE); - m_htLast = ht; - - control->Refresh(); - } - else - { - // this is not supposed to happen as the button can't go up - // without going down previously and then we'd have - // m_winCapture by now - wxFAIL_MSG( _T("logic error in mouse capturing code") ); - } - } - } - - return wxWin32InputHandler::Map(control, event); -}