I've implemented a custom wxOwnerDrawnComboBox based on the API docs. I then placed it on a wxPanel
then used SetPopupMaxHeight to limit the combo popup to show only 3 items at a time. But the popup shows an extra white row at the bottom of the popup window.
Here are screenshots (I implemented it on the minimal_cocoa sample project):
As shown, there are only 6 items but at the end of "item 5", there's an extra row.
Some notes on the issue:
SetPopupMaxHeight
Here is the code (simplified for the minimal_cocoa project):
// CUSTOM CLASS DECLARATION
class MyOwnerDrawnComboBox : public wxOwnerDrawnComboBox
{
public:
static int getItemHeight()
{
return 25;
}
public:
// ctor
MyOwnerDrawnComboBox(wxWindow* parent,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
int n = 0,
const wxString choices[] = NULL,
long style = wxODCB_STD_CONTROL_PAINT)
: wxOwnerDrawnComboBox(parent,
wxID_ANY,
wxEmptyString,
pos,
size,
n,
choices,
style,
wxDefaultValidator,
"MyOwnerDrawnComboBox")
{
}
// overrides
virtual void OnDrawItem (wxDC& dc, const wxRect& rect, int item, int flags) const
{
dc.SetTextForeground(*wxRED);
// for debugging purposes, display the item index instead
wxString label = wxString::Format("item %d", item);
dc.DrawLabel(label, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
virtual wxCoord OnMeasureItem (size_t item) const
{
return 25;
}
virtual wxCoord OnMeasureItemWidth (size_t item) const
{
return -1; // use default
}
};
// CUSTOM CLASS USAGE
wxPanel* panel = new wxPanel(this, wxID_ANY);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxString items[] = {"1", "2", "3", "4", "5", "6"}; // unused
MyOwnerDrawnComboBox* odcb = new MyOwnerDrawnComboBox(panel,
wxDefaultPosition,
wxSize(150, 30),
6,
items);
odcb->SetPopupMaxHeight(3 * MyOwnerDrawnComboBox::getItemHeight());
sizer->Add(odcb, 0, wxALL, 10);
sizer->Layout();
panel->SetSizer(sizer);
I've tried a few things:
OnDrawItem
method (same issue)OnDrawItem
(no extra calls) OnMeasureHeight
(same issue)Checked the behavior on the samples/combo/combo.cpp sample (same issue)
Some info on my wxWidgets build:
wxUSE_XTEST 1
in setup.h
wxUSE_ODCOMBOBOX
and wxUSE_COMBOCTRL
are #define
'd--with-osx_cocoa
--with-macosx-version-min=10.7
--with-macosx-sdk=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk
--enable-debug
--disable-shared
--enable-unicode
--prefix="$(pwd)"
Some info on my env:
Has anyone encountered this issue before?
I'm working around it for now by not using SetPopupMaxHeight
anymore.
If someone has a solution though, do share.
I fixed it by doing some changes to the wx source.
src/generic/vscroll.cpp
wxVarScrollHelperBase::DoScrollToUnit
:
// determine the real first unit to scroll to: we shouldn't scroll beyond
// the end
size_t unitFirstLast = FindFirstVisibleFromLast(m_unitMax - 1, true);
if ( unit > unitFirstLast )
unit = unitFirstLast;
Pass false
instead to FindFirstVisibleFromLast
.
This will allow the method to consider an item as the first item even though the last item is only partially visible ("partially" here, based on my testing, is really like "almost fully visible", with only about ~2pts that are hidden). With true
, the scroll moves down to the next item, thereby adding the extra row.
src/generic/odcombo.cpp
wxVListBoxComboPopup::OnMouseMove
:
// Only change selection if item is fully visible
if ( (y + fromBottom) >= 0 )
{
wxVListBox::SetSelection((int)line);
return;
}
To match the 2pt borders being considered in wxVListBoxComboPopup::GetAdjustedSize
, I changed the IF condition to (y + fromBottom + 2)
to allow moving the mouse over the last item in the scrolled window, even though it isn't fully visible.
src/generic/vlbox.cpp
else // line is at least partly visible
{
// it is, indeed, only partly visible, so scroll it into view to
// make it entirely visible
// BUT scrolling down when m_current is first visible makes it
// completely hidden, so that is even worse
while ( (size_t)m_current + 1 == GetVisibleRowsEnd() &&
(size_t)m_current != GetVisibleRowsBegin() &&
ScrollToRow(GetVisibleBegin() + 1) ) ;
Comment-out that while-loop to disable auto-scrolling when the mouse pointer is over the partially visible item.