From 267540d233c9e7f7213b372210da8bfff3e16c2e Mon Sep 17 00:00:00 2001 From: New Pagodi Date: Mon, 18 Mar 2019 22:55:48 -0500 Subject: [PATCH] Add AutoCompUseListCtrl method to STC The AutoCompUseListCtrl method can be used to make an autocompletion list look like it's being shown with a wxListCtrl instead of a wxListBox. When this style is used, the list will have hot tracking. On MSW, the colours will also be slightly different. --- src/stc/PlatWX.cpp | 227 ++++++++++++++++++++++++++++++++----- src/stc/PlatWX.h | 1 + src/stc/ScintillaWX.cpp | 9 ++ src/stc/ScintillaWX.h | 1 + src/stc/stc.cpp.in | 8 ++ src/stc/stc.h.in | 5 + src/stc/stc.interface.h.in | 24 ++++ 7 files changed, 248 insertions(+), 27 deletions(-) diff --git a/src/stc/PlatWX.cpp b/src/stc/PlatWX.cpp index 244960c93a..04c6e3909c 100644 --- a/src/stc/PlatWX.cpp +++ b/src/stc/PlatWX.cpp @@ -37,6 +37,7 @@ #include "wx/sizer.h" #include "wx/renderer.h" #include "wx/hashset.h" +#include "wx/dcclient.h" #ifdef wxHAS_RAW_BITMAP #include "wx/rawbmp.h" @@ -2328,6 +2329,12 @@ public: const wxColour& GetHighlightBgColour() const; const wxColour& GetHighlightTextColour() const; + // ListCtrl Style + void UseListCtrlStyle(bool, const wxColour&, const wxColour&); + bool HasListCtrlAppearance() const; + const wxColour& GetCurrentBgColour() const; + const wxColour& GetCurrentTextColour() const; + private: WX_DECLARE_HASH_MAP(int, wxBitmap, wxIntegerHash, wxIntegerEqual, ImgList); @@ -2343,12 +2350,21 @@ private: bool m_textColourIsSet; bool m_highlightBgColourIsSet; bool m_highlightTextColourIsSet; + + bool m_hasListCtrlAppearance; + wxColour m_currentBgColour; + wxColour m_currentTextColour; + bool m_currentBgColourIsSet; + bool m_currentTextColourIsSet; }; wxSTCListBoxVisualData::wxSTCListBoxVisualData(int d):m_desiredVisibleRows(d), m_bgColourIsSet(false), m_textColourIsSet(false), m_highlightBgColourIsSet(false), - m_highlightTextColourIsSet(false) + m_highlightTextColourIsSet(false), + m_hasListCtrlAppearance(false), + m_currentBgColourIsSet(false), + m_currentTextColourIsSet(false) { ComputeColours(); } @@ -2434,18 +2450,46 @@ void wxSTCListBoxVisualData::ComputeColours() if ( !m_textColourIsSet ) m_textColour = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT); -#ifdef __WXOSX_COCOA__ - if ( !m_highlightBgColourIsSet ) - m_highlightBgColour = GetListHighlightColour(); -#else - if ( !m_highlightBgColourIsSet ) - m_highlightBgColour = - wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); -#endif + if ( m_hasListCtrlAppearance ) + { + // If m_highlightBgColour and/or m_currentBgColour are not + // explicitly set, set them to wxNullColour to indicate that they + // should be drawn with wxRendererNative. + if ( !m_highlightBgColourIsSet ) + m_highlightBgColour = wxNullColour; - if ( !m_highlightTextColourIsSet ) - m_highlightTextColour = - wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT); + if ( !m_currentBgColourIsSet ) + m_currentBgColour = wxNullColour; + + #ifdef __WXMSW__ + if ( !m_highlightTextColourIsSet ) + m_highlightTextColour = + wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT); + #else + if ( !m_highlightTextColourIsSet ) + m_highlightTextColour = wxSystemSettings::GetColour( + wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT); + #endif + + if ( !m_currentTextColour.IsOk() ) + m_currentTextColour = wxSystemSettings::GetColour( + wxSYS_COLOUR_LISTBOXTEXT); + } + else + { + #ifdef __WXOSX_COCOA__ + if ( !m_highlightBgColourIsSet ) + m_highlightBgColour = GetListHighlightColour(); + #else + if ( !m_highlightBgColourIsSet ) + m_highlightBgColour = + wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); + #endif + + if ( !m_highlightTextColourIsSet ) + m_highlightTextColour = + wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT); + } } void SetColourHelper(bool& isSet, wxColour& itemCol, const wxColour& newColour) @@ -2491,10 +2535,34 @@ const wxColour& wxSTCListBoxVisualData::GetHighlightTextColour() const return m_highlightTextColour; } +void wxSTCListBoxVisualData::UseListCtrlStyle(bool useListCtrlStyle, + const wxColour& curBg, + const wxColour& curText) +{ + m_hasListCtrlAppearance = useListCtrlStyle; + SetColourHelper(m_currentBgColourIsSet, m_currentBgColour, curBg); + SetColourHelper(m_currentTextColourIsSet, m_currentTextColour, curText); + ComputeColours(); +} + +bool wxSTCListBoxVisualData::HasListCtrlAppearance() const +{ + return m_hasListCtrlAppearance; +} + +const wxColour& wxSTCListBoxVisualData::GetCurrentBgColour() const +{ + return m_currentBgColour; +} + +const wxColour& wxSTCListBoxVisualData::GetCurrentTextColour() const +{ + return m_currentTextColour; +} // The class is intended to look like a standard listbox (with an optional // icon). However, it needs to look like it has focus even when it doesn't. -class wxSTCListBox : public wxVListBox +class wxSTCListBox : public wxSystemThemedControl { public: wxSTCListBox(wxWindow*, wxSTCListBoxVisualData*, int); @@ -2530,6 +2598,8 @@ protected: // Event handlers void OnDClick(wxCommandEvent&); void OnSysColourChanged(wxSysColourChangedEvent& event); + void OnMouseMotion(wxMouseEvent& event); + void OnMouseLeaveWindow(wxMouseEvent& event); // wxVListBox overrides virtual wxCoord OnMeasureItem(size_t) const wxOVERRIDE; @@ -2543,6 +2613,7 @@ private: wxVector m_labels; wxVector m_imageNos; size_t m_maxStrWidth; + int m_currentRow; CallBackAction m_doubleClickAction; void* m_doubleClickActionData; @@ -2564,7 +2635,8 @@ private: }; wxSTCListBox::wxSTCListBox(wxWindow* parent, wxSTCListBoxVisualData* v, int ht) - :wxVListBox(), m_visualData(v), m_maxStrWidth(0), + :wxSystemThemedControl(), + m_visualData(v), m_maxStrWidth(0), m_currentRow(wxNOT_FOUND), m_doubleClickAction(NULL), m_doubleClickActionData(NULL), m_aveCharWidth(8), m_textHeight(ht), m_itemHeight(ht), m_textTopGap(0), m_imageAreaWidth(0), m_imageAreaHeight(0) @@ -2580,6 +2652,20 @@ wxSTCListBox::wxSTCListBox(wxWindow* parent, wxSTCListBoxVisualData* v, int ht) Bind(wxEVT_LISTBOX_DCLICK, &wxSTCListBox::OnDClick, this); Bind(wxEVT_SYS_COLOUR_CHANGED, &wxSTCListBox::OnSysColourChanged, this); + + if ( m_visualData->HasListCtrlAppearance() ) + { + EnableSystemTheme(); + Bind(wxEVT_MOTION, &wxSTCListBox::OnMouseMotion, this); + Bind(wxEVT_LEAVE_WINDOW, &wxSTCListBox::OnMouseLeaveWindow, this); + + #ifdef __WXMSW__ + // On MSW when using wxRendererNative to draw items in list control + // style, the colours used seem to be based on the parent's + // background colour. So set the popup's background. + parent->SetOwnBackgroundColour(m_visualData->GetBgColour()); + #endif + } } wxSTCListBox::~wxSTCListBox() @@ -2771,8 +2857,37 @@ void wxSTCListBox::OnDClick(wxCommandEvent& WXUNUSED(event)) void wxSTCListBox::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event)) { m_visualData->ComputeColours(); + GetParent()->SetOwnBackgroundColour(m_visualData->GetBgColour()); SetBackgroundColour(m_visualData->GetBgColour()); - Refresh(); + GetParent()->Refresh(); +} + +void wxSTCListBox::OnMouseLeaveWindow(wxMouseEvent& event) +{ + const int old = m_currentRow; + m_currentRow = wxNOT_FOUND; + + if ( old != wxNOT_FOUND ) + RefreshRow(old); + + event.Skip(); +} + +void wxSTCListBox::OnMouseMotion(wxMouseEvent& event) +{ + const int old = m_currentRow; + m_currentRow = VirtualHitTest(event.GetY()); + + if ( old != m_currentRow ) + { + if( m_currentRow != wxNOT_FOUND ) + RefreshRow(m_currentRow); + + if( old != wxNOT_FOUND ) + RefreshRow(old); + } + + event.Skip(); } wxCoord wxSTCListBox::OnMeasureItem(size_t WXUNUSED(n)) const @@ -2821,6 +2936,8 @@ void wxSTCListBox::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const if ( IsSelected(n) ) tcc.Set(m_visualData->GetHighlightTextColour()); + else if ( static_cast(n) == m_currentRow ) + tcc.Set(m_visualData->GetCurrentTextColour()); else tcc.Set(m_visualData->GetTextColour()); @@ -2845,19 +2962,50 @@ void wxSTCListBox::OnDrawBackground(wxDC &dc, const wxRect &rect,size_t n) const const wxColour& highlightBgColour =m_visualData->GetHighlightBgColour(); #ifdef __WXMSW__ - // On windows the selection rectangle in Scintilla's - // autocompletion list only covers the text and not the icon. - const int textBoxFromClientEdge = TextBoxFromClientEdge(); - selectionRect.SetLeft(rect.GetLeft() + textBoxFromClientEdge); - selectionRect.SetWidth(rect.GetWidth() - textBoxFromClientEdge); + if ( !m_visualData->HasListCtrlAppearance() ) + { + // On windows the selection rectangle in Scintilla's + // autocompletion list only covers the text and not the icon. + + const int textBoxFromClientEdge = TextBoxFromClientEdge(); + selectionRect.SetLeft(rect.GetLeft() + textBoxFromClientEdge); + selectionRect.SetWidth(rect.GetWidth() - textBoxFromClientEdge); + } #endif // __WXMSW__ - wxDCBrushChanger bc(dc, highlightBgColour); - wxDCPenChanger pc(dc, highlightBgColour); - dc.DrawRectangle(selectionRect); + if ( highlightBgColour.IsOk() ) + { + wxDCBrushChanger bc(dc, highlightBgColour); + wxDCPenChanger pc(dc, highlightBgColour); + dc.DrawRectangle(selectionRect); + } + else + { + wxRendererNative::GetDefault().DrawItemSelectionRect( + const_cast(this), dc, selectionRect, + wxCONTROL_SELECTED | wxCONTROL_FOCUSED); + } - wxRendererNative::GetDefault().DrawFocusRect( - const_cast(this), dc, selectionRect); + if ( !m_visualData->HasListCtrlAppearance() ) + wxRendererNative::GetDefault().DrawFocusRect( + const_cast(this), dc, selectionRect); + } + else if ( static_cast(n) == m_currentRow ) + { + const wxColour& currentBgColour = m_visualData->GetCurrentBgColour(); + + if ( currentBgColour.IsOk() ) + { + wxDCBrushChanger bc(dc, currentBgColour); + wxDCPenChanger pc(dc, currentBgColour); + dc.DrawRectangle(rect); + } + else + { + wxRendererNative::GetDefault().DrawItemSelectionRect( + const_cast(this), dc, rect, + wxCONTROL_CURRENT | wxCONTROL_FOCUSED); + } } } @@ -2867,6 +3015,12 @@ class wxSTCListBoxWin : public wxSTCPopupWindow { public: wxSTCListBoxWin(wxWindow*, wxSTCListBox**, wxSTCListBoxVisualData*, int); + +protected: + void OnPaint(wxPaintEvent&); + +private: + wxSTCListBoxVisualData* m_visualData; }; wxSTCListBoxWin::wxSTCListBoxWin(wxWindow* parent, wxSTCListBox** lb, @@ -2885,9 +3039,22 @@ wxSTCListBoxWin::wxSTCListBoxWin(wxWindow* parent, wxSTCListBox** lb, wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL); bSizer->Add(*lb, 1, wxEXPAND|wxALL, borderThickness); SetSizer(bSizer); - (*lb)->SetContainerBorderSize(borderThickness); - SetOwnBackgroundColour(v->GetBorderColour()); + + // When drawing highlighting in listctrl style with wxRendererNative on MSW, + // the colours used seem to be based on the background of the parent window. + // So manually paint this window to give it the border colour instead of + // setting the background colour. + m_visualData = v; + Bind(wxEVT_PAINT, &wxSTCListBoxWin::OnPaint, this); + SetBackgroundStyle(wxBG_STYLE_PAINT); +} + +void wxSTCListBoxWin::OnPaint(wxPaintEvent& WXUNUSED(evt)) +{ + wxPaintDC dc(this); + dc.SetBackground(m_visualData->GetBorderColour()); + dc.Clear(); } @@ -3014,6 +3181,12 @@ void ListBoxImpl::SetColours(const wxColour& background, const wxColour& text, m_visualData->SetColours(background, text, hiliBg, hiliText); } +void ListBoxImpl::UseListCtrlStyle(bool useListCtrl, const wxColour& currentBg, + const wxColour& currentText) +{ + m_visualData->UseListCtrlStyle(useListCtrl, currentBg, currentText); +} + ListBox::ListBox() { } diff --git a/src/stc/PlatWX.h b/src/stc/PlatWX.h index 2540e5e927..8449c870d2 100644 --- a/src/stc/PlatWX.h +++ b/src/stc/PlatWX.h @@ -51,6 +51,7 @@ public: virtual void SetList(const char* list, char separator, char typesep) wxOVERRIDE; void SetColours(const wxColour&, const wxColour&, const wxColour&, const wxColour&); + void UseListCtrlStyle(bool, const wxColour&, const wxColour&); }; diff --git a/src/stc/ScintillaWX.cpp b/src/stc/ScintillaWX.cpp index 7c5d1e5320..14172d1719 100644 --- a/src/stc/ScintillaWX.cpp +++ b/src/stc/ScintillaWX.cpp @@ -1346,6 +1346,15 @@ void ScintillaWX::SetListBoxColours(const wxColour& background, highlight, highlightText); } +void ScintillaWX::UseListCtrlStyleForLists(bool useListCtrl, + const wxColour& currentBgColour, + const wxColour& currentTextColour) +{ + static_cast(ac.lb)->UseListCtrlStyle(useListCtrl, + currentBgColour, + currentTextColour); +} + sptr_t ScintillaWX::DirectFunction( ScintillaWX* swx, unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return swx->WndProc(iMessage, wParam, lParam); diff --git a/src/stc/ScintillaWX.h b/src/stc/ScintillaWX.h index a4ab484b10..5bbe6179c3 100644 --- a/src/stc/ScintillaWX.h +++ b/src/stc/ScintillaWX.h @@ -203,6 +203,7 @@ public: void DoRegisterImage(int type, const wxBitmap& bmp); void SetListBoxColours(const wxColour&, const wxColour&, const wxColour&, const wxColour&); + void UseListCtrlStyleForLists(bool, const wxColour&, const wxColour&); private: bool capturedMouse; diff --git a/src/stc/stc.cpp.in b/src/stc/stc.cpp.in index e6cb53a92e..91807cf3f9 100644 --- a/src/stc/stc.cpp.in +++ b/src/stc/stc.cpp.in @@ -581,6 +581,14 @@ void wxStyledTextCtrl::AutoCompSetColours(const wxColour& background, m_swx->SetListBoxColours(background, text, highlight, highlightText); } +void wxStyledTextCtrl::AutoCompUseListCtrl(bool useListCtrl, + const wxColour& currentBgColour, + const wxColour& currentTextColour) +{ + m_swx->UseListCtrlStyleForLists(useListCtrl, currentBgColour, + currentTextColour); +} + diff --git a/src/stc/stc.h.in b/src/stc/stc.h.in index 4c75e1589a..ea04d4f60c 100644 --- a/src/stc/stc.h.in +++ b/src/stc/stc.h.in @@ -309,6 +309,11 @@ public: const wxColour& highlight, const wxColour& highlightText); + // Use a wxListCtrl to display autocompletion lists. + void AutoCompUseListCtrl(bool useListCtrl = true, + const wxColour& currentBgColour = wxNullColour, + const wxColour& currentTextColour = wxNullColour); + // The following methods are nearly equivalent to their similarly named diff --git a/src/stc/stc.interface.h.in b/src/stc/stc.interface.h.in index e21f40f546..a3c0334908 100644 --- a/src/stc/stc.interface.h.in +++ b/src/stc/stc.interface.h.in @@ -386,6 +386,30 @@ public: void AutoCompSetColours(const wxColour& background, const wxColour& text, const wxColour& highlight, const wxColour& highlightText); + + /** + Use a wxListCtrl to display autocompletion and user lists. + + By default lists will be displayed in a wxListBox. Use this method to + display them in a wxListCtrl instead. The primary difference is that + wxListCtrl has hot tracking to highlight the item under the mouse cursor. + @param useListCtrl + Set this to true to use a wxListCtrl and to false to use a + wxListBox. + @param currentBgColour + The colour used to highlight the item under the mouse cursor. + @param currentTextColour + The colour used for the text of the item under the mouse cursor. + @remarks + To reset one or more of the colours to its default, + call this method with wxNullColour for the colour or colours + to be reset. + + @since 3.1.3 + */ + void AutoCompUseListCtrl(bool useListCtrl = true, + const wxColour& currentBgColour = wxNullColour, + const wxColour& currentTextColour = wxNullColour); //@}