From 075fa2b764497738e890ef0a47b91fd65cd3eba2 Mon Sep 17 00:00:00 2001 From: New Pagodi Date: Tue, 6 Aug 2019 18:19:58 -0500 Subject: [PATCH 1/3] Record wxSTCPopupWindow position every time its set The position of wxSTCPopupWindow is currently recorded the first time it is set so that it can be used to reposition the popup when its parent window is moved. However Scintilla apparently positions the popup at least twice when icons are used and the first set position is slightly wrong. Instead just record the position every time its set by Scintilla. --- src/stc/PlatWX.cpp | 10 ++++------ src/stc/PlatWX.h | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/stc/PlatWX.cpp b/src/stc/PlatWX.cpp index 11795fe591..03b33aaadc 100644 --- a/src/stc/PlatWX.cpp +++ b/src/stc/PlatWX.cpp @@ -2224,7 +2224,7 @@ PRectangle Window::GetMonitorRect(Point pt) { #endif // __WXOSX_COCOA__ wxSTCPopupWindow::wxSTCPopupWindow(wxWindow* parent) - :wxSTCPopupBase(parent), m_initialPosition(wxDefaultPosition) + :wxSTCPopupBase(parent), m_lastKnownPosition(wxDefaultPosition) { #if !wxSTC_POPUP_IS_CUSTOM Bind(wxEVT_SET_FOCUS, &wxSTCPopupWindow::OnFocus, this); @@ -2275,9 +2275,7 @@ bool wxSTCPopupWindow::AcceptsFocus() const void wxSTCPopupWindow::DoSetSize(int x, int y, int width, int height, int flags) { - if ( m_initialPosition == wxDefaultPosition - && x != wxDefaultCoord && y != wxDefaultCoord ) - m_initialPosition = wxPoint(x, y); + m_lastKnownPosition = wxPoint(x, y); // convert coords to screen coords since we're a top-level window if (x != wxDefaultCoord) @@ -2291,8 +2289,8 @@ void wxSTCPopupWindow::DoSetSize(int x, int y, int width, int height, int flags) void wxSTCPopupWindow::OnParentMove(wxMoveEvent& event) { - if ( m_initialPosition != wxDefaultPosition ) - SetPosition(m_initialPosition); + if ( m_lastKnownPosition.IsFullySpecified() ) + SetPosition(m_lastKnownPosition); event.Skip(); } diff --git a/src/stc/PlatWX.h b/src/stc/PlatWX.h index 714f5e4180..31a11dbf4d 100644 --- a/src/stc/PlatWX.h +++ b/src/stc/PlatWX.h @@ -150,7 +150,7 @@ protected: #endif private: - wxPoint m_initialPosition; + wxPoint m_lastKnownPosition; wxWindow* m_tlw; }; From c3d9222a7cba7d8ef44adbb6ec827e868f450d39 Mon Sep 17 00:00:00 2001 From: New Pagodi Date: Wed, 7 Aug 2019 21:33:17 -0500 Subject: [PATCH 2/3] Fix wxSTCPopupBase::Show for windows The existing logic for the Show method results in wxWindowBase::Show being called twice when the argument is false. Slightly rearrange the code to fix this defect. --- src/stc/PlatWX.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/stc/PlatWX.cpp b/src/stc/PlatWX.cpp index 03b33aaadc..f122270e75 100644 --- a/src/stc/PlatWX.cpp +++ b/src/stc/PlatWX.cpp @@ -2117,24 +2117,27 @@ PRectangle Window::GetMonitorRect(Point pt) { // Do not activate the window when it is shown. bool wxSTCPopupBase::Show(bool show) { - if ( !wxWindowBase::Show(show) ) - return false; - if ( show ) { - HWND hWnd = reinterpret_cast(GetHandle()); - if ( GetName() == "wxSTCCallTip" ) - ::AnimateWindow(hWnd, 25, AW_BLEND); - else - ::ShowWindow(hWnd, SW_SHOWNA ); + // Check if the window is changing from hidden to shown. + bool changingVisibility = wxWindowBase::Show(true); - ::SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + if ( changingVisibility ) + { + HWND hWnd = reinterpret_cast(GetHandle()); + if ( GetName() == "wxSTCCallTip" ) + ::AnimateWindow(hWnd, 25, AW_BLEND); + else + ::ShowWindow(hWnd, SW_SHOWNA ); + + ::SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + + return changingVisibility; } else - wxPopupWindow::Show(false); - - return true; + return wxPopupWindow::Show(false); } // Do not activate in response to mouse clicks on this window. From 4523df5d9459f5e43ad2d04a5e79096a34d2b392 Mon Sep 17 00:00:00 2001 From: New Pagodi Date: Wed, 7 Aug 2019 21:35:45 -0500 Subject: [PATCH 3/3] Redirect mouse wheel events when STC autocomp is active While an autocompletion list is being shown, a native Scintilla window will use mouse wheel events to scroll the list instead of the editor window. Match this behavior with wxSTC by intercepting mouse wheel events and redirecting them to the list. --- src/stc/PlatWX.cpp | 2 +- src/stc/stc.cpp | 96 ++++++++++++++++++++++++++++++++++++++-------- src/stc/stc.cpp.in | 96 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 159 insertions(+), 35 deletions(-) diff --git a/src/stc/PlatWX.cpp b/src/stc/PlatWX.cpp index f122270e75..359b5c4383 100644 --- a/src/stc/PlatWX.cpp +++ b/src/stc/PlatWX.cpp @@ -2701,7 +2701,7 @@ wxSTCListBox::wxSTCListBox(wxWindow* parent, wxSTCListBoxVisualData* v, int ht) m_textTopGap(0), m_imageAreaWidth(0), m_imageAreaHeight(0) { wxVListBox::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxBORDER_NONE); + wxBORDER_NONE, "AutoCompListBox"); m_imagePadding = FromDIP(1); m_textBoxToTextGap = FromDIP(3); diff --git a/src/stc/stc.cpp b/src/stc/stc.cpp index 789b44caf7..65a7a74684 100644 --- a/src/stc/stc.cpp +++ b/src/stc/stc.cpp @@ -46,6 +46,8 @@ #include "wx/tokenzr.h" #include "wx/mstream.h" #include "wx/image.h" +#include "wx/vlbox.h" +#include "wx/stack.h" #if wxUSE_FFILE #include "wx/ffile.h" #elif wxUSE_FILE @@ -5251,29 +5253,89 @@ void wxStyledTextCtrl::OnContextMenu(wxContextMenuEvent& evt) { void wxStyledTextCtrl::OnMouseWheel(wxMouseEvent& evt) { + // The default action of this method is to call m_swx->DoMouseWheel. + // However, it might be necessary to do something else depending on whether + // 1) the mouse wheel captures for the STC, + // 2) the event's position is in the STC's rect, and + // 3) and an autocompletion list is currently being shown. + // This table summarizes when each action is needed. + + // InRect | MouseWheelCaptures | Autocomp Active | action + // -------+--------------------+-----------------+------------------- + // true | true | true | scroll ac list + // true | true | false | default + // true | false | true | scroll ac list + // true | false | false | default + // false | true | true | scroll ac list + // false | true | false | default + // false | false | true | forward to parent + // false | false | false | forward to parent + // if the mouse wheel is not captured, test if the mouse // pointer is over the editor window and if not, don't // handle the message but pass it on. - if ( !GetMouseWheelCaptures() ) { - if ( !GetRect().Contains(evt.GetPosition()) ) { - wxWindow* parent = GetParent(); - if (parent != NULL) { - wxMouseEvent newevt(evt); - newevt.SetPosition( - parent->ScreenToClient(ClientToScreen(evt.GetPosition()))); - parent->ProcessWindowEvent(newevt); - } - return; + if ( !GetMouseWheelCaptures() && !GetRect().Contains(evt.GetPosition()) ) + { + wxWindow* parent = GetParent(); + if ( parent != NULL ) + { + wxMouseEvent newevt(evt); + newevt.SetPosition( + parent->ScreenToClient(ClientToScreen(evt.GetPosition()))); + parent->ProcessWindowEvent(newevt); } } + else if ( AutoCompActive() ) + { + // When the autocompletion popup is active, Scintilla uses the mouse + // wheel to scroll the autocomp list instead of the editor. - m_swx->DoMouseWheel(evt.GetWheelAxis(), - evt.GetWheelRotation(), - evt.GetWheelDelta(), - evt.GetLinesPerAction(), - evt.GetColumnsPerAction(), - evt.ControlDown(), - evt.IsPageScroll()); + // First try to find the list. It will be a wxVListBox named + // "AutoCompListBox". + wxWindow* curWin = this, *acListBox = NULL; + wxStack windows; + windows.push(curWin); + + while ( !windows.empty() ) + { + curWin = windows.top(); + windows.pop(); + + if ( curWin->IsKindOf(wxCLASSINFO(wxVListBox)) && + curWin->GetName() == "AutoCompListBox") + { + acListBox = curWin; + break; + } + + wxWindowList& children = curWin->GetChildren(); + wxWindowList::iterator it; + + for ( it = children.begin(); it!=children.end(); ++it ) + { + windows.push(*it); + } + } + + // Next if the list was found, send it a copy of this event. + if ( acListBox ) + { + wxMouseEvent newevt(evt); + newevt.SetPosition( + acListBox->ScreenToClient(ClientToScreen(evt.GetPosition()))); + acListBox->ProcessWindowEvent(newevt); + } + } + else + { + m_swx->DoMouseWheel(evt.GetWheelAxis(), + evt.GetWheelRotation(), + evt.GetWheelDelta(), + evt.GetLinesPerAction(), + evt.GetColumnsPerAction(), + evt.ControlDown(), + evt.IsPageScroll()); + } } diff --git a/src/stc/stc.cpp.in b/src/stc/stc.cpp.in index 833b8c9a33..198c4ccac4 100644 --- a/src/stc/stc.cpp.in +++ b/src/stc/stc.cpp.in @@ -46,6 +46,8 @@ #include "wx/tokenzr.h" #include "wx/mstream.h" #include "wx/image.h" +#include "wx/vlbox.h" +#include "wx/stack.h" #if wxUSE_FFILE #include "wx/ffile.h" #elif wxUSE_FILE @@ -778,29 +780,89 @@ void wxStyledTextCtrl::OnContextMenu(wxContextMenuEvent& evt) { void wxStyledTextCtrl::OnMouseWheel(wxMouseEvent& evt) { + // The default action of this method is to call m_swx->DoMouseWheel. + // However, it might be necessary to do something else depending on whether + // 1) the mouse wheel captures for the STC, + // 2) the event's position is in the STC's rect, and + // 3) and an autocompletion list is currently being shown. + // This table summarizes when each action is needed. + + // InRect | MouseWheelCaptures | Autocomp Active | action + // -------+--------------------+-----------------+------------------- + // true | true | true | scroll ac list + // true | true | false | default + // true | false | true | scroll ac list + // true | false | false | default + // false | true | true | scroll ac list + // false | true | false | default + // false | false | true | forward to parent + // false | false | false | forward to parent + // if the mouse wheel is not captured, test if the mouse // pointer is over the editor window and if not, don't // handle the message but pass it on. - if ( !GetMouseWheelCaptures() ) { - if ( !GetRect().Contains(evt.GetPosition()) ) { - wxWindow* parent = GetParent(); - if (parent != NULL) { - wxMouseEvent newevt(evt); - newevt.SetPosition( - parent->ScreenToClient(ClientToScreen(evt.GetPosition()))); - parent->ProcessWindowEvent(newevt); - } - return; + if ( !GetMouseWheelCaptures() && !GetRect().Contains(evt.GetPosition()) ) + { + wxWindow* parent = GetParent(); + if ( parent != NULL ) + { + wxMouseEvent newevt(evt); + newevt.SetPosition( + parent->ScreenToClient(ClientToScreen(evt.GetPosition()))); + parent->ProcessWindowEvent(newevt); } } + else if ( AutoCompActive() ) + { + // When the autocompletion popup is active, Scintilla uses the mouse + // wheel to scroll the autocomp list instead of the editor. - m_swx->DoMouseWheel(evt.GetWheelAxis(), - evt.GetWheelRotation(), - evt.GetWheelDelta(), - evt.GetLinesPerAction(), - evt.GetColumnsPerAction(), - evt.ControlDown(), - evt.IsPageScroll()); + // First try to find the list. It will be a wxVListBox named + // "AutoCompListBox". + wxWindow* curWin = this, *acListBox = NULL; + wxStack windows; + windows.push(curWin); + + while ( !windows.empty() ) + { + curWin = windows.top(); + windows.pop(); + + if ( curWin->IsKindOf(wxCLASSINFO(wxVListBox)) && + curWin->GetName() == "AutoCompListBox") + { + acListBox = curWin; + break; + } + + wxWindowList& children = curWin->GetChildren(); + wxWindowList::iterator it; + + for ( it = children.begin(); it!=children.end(); ++it ) + { + windows.push(*it); + } + } + + // Next if the list was found, send it a copy of this event. + if ( acListBox ) + { + wxMouseEvent newevt(evt); + newevt.SetPosition( + acListBox->ScreenToClient(ClientToScreen(evt.GetPosition()))); + acListBox->ProcessWindowEvent(newevt); + } + } + else + { + m_swx->DoMouseWheel(evt.GetWheelAxis(), + evt.GetWheelRotation(), + evt.GetWheelDelta(), + evt.GetLinesPerAction(), + evt.GetColumnsPerAction(), + evt.ControlDown(), + evt.IsPageScroll()); + } }