When using SetItemCount()
to add items to a virtual wxListCtrl that already contains many items (millions), the calculated range of visible items is slightly different on each repaint. This causes the range of visible items to "dance" around on screen.
I believe this is because, in the wxMAC implementation, the visible range is calculated by using the native control position value in range [0,1] times the maximum number of items in the list. This value is computed and set as a double:
void SetScrollThumb( wxInt32 value, wxInt32 thumbSize ) override
{
double v = ((double) value)/m_maximum;
double t = ((double) thumbSize)/(m_maximum+thumbSize);
[(wxNSScroller*) m_osxView setDoubleValue:v];
[(wxNSScroller*) m_osxView setKnobProportion:t];
}
I believe the issue is that GetValue()
retrieves it as a float and rounds, leading to issues with numerical precision.
virtual wxInt32 GetValue() const override
{
return wxRound([(wxNSScroller*) m_osxView floatValue] * m_maximum);
}
Using doubleValue
appears to fix the behavior. I briefly tried updating wxOSXScrollBarCocoaImpl to track its integer position as a member variable directly, which seems like a more robust solution, but I wasn't quite sure how to update the integer position a result of click-and-drag on the scroll thumb.
diff --git a/src/osx/cocoa/scrolbar.mm b/src/osx/cocoa/scrolbar.mm
index ed77223596..58d1f5d407 100644
--- a/src/osx/cocoa/scrolbar.mm
+++ b/src/osx/cocoa/scrolbar.mm
@@ -62,7 +62,7 @@ void SetScrollThumb( wxInt32 value, wxInt32 thumbSize ) override
virtual wxInt32 GetValue() const override
{
- return wxRound([(wxNSScroller*) m_osxView floatValue] * m_maximum);
+ return wxRound([(wxNSScroller*) m_osxView doubleValue] * m_maximum);
}
I would expect the visible range of items to not change, as additional items are added through SetItemCount()
.
#include <wx/listctrl.h>
#include <wx/timer.h>
#include <wx/wx.h>
const long itemcount = 20000000;
class MyListCtrl : public wxListCtrl {
public:
MyListCtrl(wxWindow* parent, wxWindowID id)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_VIRTUAL) {
AppendColumn("Column", wxLIST_FORMAT_LEFT);
SetItemCount(itemcount + 100);
EnsureVisible(itemcount);
}
wxString OnGetItemText(long item, long column) const override {
return wxString::Format("Item %ld", item);
}
};
class MyFrame : public wxFrame {
public:
MyListCtrl *listCtrl;
MyFrame() : wxFrame(NULL, wxID_ANY, "example") {
listCtrl = new MyListCtrl(this, wxID_ANY);
wxTimer* timer = new wxTimer(this, 3 /*timer ID*/);
Bind(wxEVT_TIMER, &MyFrame::OnTimer, this, 3 /*timer ID*/);
timer->Start(300);
}
void OnTimer(wxTimerEvent& e) {
static auto i = 0;
listCtrl->SetItemCount(itemcount + (i++));
}
};
class MyApp : public wxApp {
public:
virtual bool OnInit() {
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
};
wxIMPLEMENT_APP(MyApp);
Compile above program, and run. The GUI will update on a timer and the set of visible items will dance around a bit. The window may need to be resized a bit to trigger the behavior, if the behavior is not observed.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.