The wxDataViewTreeCtrl control is rendered backwards/mirrored when in right-to-left layout mode on Windows. The problem appeared in 3.2.9.
The incorrect appearance:
image.png (view on web)
Expected appearance using 3.2.8:
image.png (view on web)
Windows 11 25H2 x64
wxWidgets 3.2.10 wxMSW
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
This is really strange, I don't see anything obviously responsible for this in the list of commits between 3.2.8 and 3.2.9. The only commits touching wxDVC are 9de810e (Improve padding when using HiDPI in generic wxDataViewCtrl, 2025-10-15), 4704189 (Avoid collapsing wxDVC node if already done by event handler, 2025-07-14) and eec195c (Implement GetMainWindowOfCompositeControl for wxDataViewCtrl, 2025-05-07) but they don't seem to have anything to do with this.
If you can run git-bisect to find the exact commit which broke this, it would be most helpful.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
I am curious to know which commit breaks RTL in wx 3.2.9; however, the following patch should fix this issue in 3.2.9 as well as in master:
diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 7ae8c2a21e..b24b5a3c2c 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2380,6 +2380,10 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, ysrc = hDIB - (ysrc + srcHeight); } + // Force LTR layout for correct rendering in RTL layout + const auto oldLayoutDir = GetLayoutDirection(); + SetLayoutDirection(wxLayout_LeftToRight); + if ( ::StretchDIBits(GetHdc(), xdest, ydest, dstWidth, dstHeight, @@ -2397,6 +2401,8 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, { success = true; } + + SetLayoutDirection(oldLayoutDir); } }
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Sorry for the bad news, but anything using wxAutoBufferedPaintDC (which is a wxBufferedPaintDC) is broken in RTL layout under wxMSW in master!!!
What needs to be fixed then is wxBufferedPaintDC because using native auto bufferring (WS_EX_COMPOSITED) makes RTL working correctly.
I'll invistigate...
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
The immediate culprit is 46d47d3
That’s not the whole story, though. wxDVC RTL rendering is broken at least all the way back to 3.2.2 (I didn’t test further); I suspect this commit just made it worse. The breakage manifests in weird, hard to reproduce ways. I was initially confused that I couldn’t reproduce a user report matching this issue exactly - mirrored content. Instead, I got black rectangle.
Reverting 46d47d3 helped - I could see the first shown wxDVC fine now. Until I opened another wxDVC, which showed as black, and then re-opening the window that initially worked also showed as black from then on — the “bad” wxDVC instance would corrupt everything until I restarted the app. For background, I run Windows at 200% scale, on a 5120x2880 display. I eventually realized that size matters: everything would work with smaller windows at 100%. So far I did see:
I realize this is all hand-wavy and vague, but I don’t have better understanding (yet, anyway).
tl;dr: I think @AliKet is right to suspect wxBufferedPaintDC but 3.2.8 → 3.2.9 is a red herring; the actual underlying bug is much older.
For completeness, that patch doesn’t seem to have any observable effect here.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Surprisingly, the following patch fixes the issue for me (tested with master), though I still don't have a satisfactory explanation for it.
diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 7ae8c2a21e..8cb3ac8c71 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2366,10 +2366,12 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, // automatically as it doesn't even work with the source HDC. // So do this manually to ensure that the coordinates are // interpreted in the same way here as in all the other cases. - xsrc = source->LogicalToDeviceX(xsrcOrig); - ysrc = source->LogicalToDeviceY(ysrcOrig); - srcWidth = source->LogicalToDeviceXRel(srcWidth); - srcHeight = source->LogicalToDeviceYRel(srcHeight); + const auto pt = source->LogicalToDevice(xsrcOrig, ysrcOrig); + const auto sz = source->LogicalToDeviceRel(srcWidth, srcHeight); + xsrc = pt.x; + ysrc = pt.y; + srcWidth = sz.x; + srcHeight = sz.y; // Figure out what co-ordinate system we're supposed to specify // ysrc in.
BTW, the auidemo is also affected.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Confirmed on 3.2 too, can’t reproduce any of the things I described above anymore — with just the patch above, not reverting 46d47d3
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Thanks, so the problem is that wxDC::DeviceToLogical[XY]{,Rel}() are broken in wxMSW when using RTL, right? We can reimplement them in terms of the 2-component version implemented natively, of course, but I'm not sure what exactly is wrong: is it just the missing multiplication by the axis sign or something else?
Can you please look at the difference between the results of the 2 sets of functions under debugger or add a wxLogDebug() call showing them?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Confirmed on 3.2 too, can’t reproduce any of the things I described above anymore — with just the patch above, not reverting 46d47d3
Thank you for testing and confirming that the last patch works. There is one small correction needed, however: the xsrc, ysrc, srcWidth, and srcHeight parameters passed to StretchDIBits() must be specified in logical units, not device units, i.e: we should use
DeviceToLogical() instead of LogicalToDevice() and
DeviceToLogicalRel() instead of LogicalToDeviceRel()
Because otherwise scrolling is totally broken as can be seen in the dataview sample. auidemo and grid samples are also affected.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Because otherwise scrolling is totally broken as can be seen in the dataview sample
If you mean it like this
const auto pt = source->DeviceToLogical(xsrcOrig, ysrcOrig); const auto sz = source->DeviceToLogicalRel(srcWidth, srcHeight);
then I see the opposite on 3.2: this breaks scrolling in wxDVC, but it worked with the original version.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Because otherwise scrolling is totally broken as can be seen in the dataview sample
If you mean it like this
const auto pt = source->DeviceToLogical(xsrcOrig, ysrcOrig);
const auto sz = source->DeviceToLogicalRel(srcWidth, srcHeight);
Yes
then I see the opposite on 3.2: this breaks scrolling in wxDVC, but it worked with the original version.
I switched to v3.2.10 and none of the patches fix the scrolling issue. But with the last patch everything works correctly for me after reverting this commit 288b208 as is done in master in this commit 0387253 and also this one f5d326f
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
This is how it works (scrolling dataview sample) under my system:
[Bas] :
https://github.com/user-attachments/assets/48e9d078-3e9e-4b37-829a-b6d5d5ead836
[Good] :
https://github.com/user-attachments/assets/e86e7e95-ea05-43e5-ad3d-54084b8524eb
The last video is the result of applying the fixes I mentioned in the comment above.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Sorry for making some noise here, but I think I finally managed to fix this issue (in master at least) with this patch, which works for me in both layouts: LTR as well as RTL:
diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 7ae8c2a21e..756fc44ee5 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -2145,7 +2145,9 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, xdest += XLOG2DEV(0); ydest += YLOG2DEV(0); - const int xsrcOrig = xsrc; + // As m_signX is always positive regardless of the layout, the value returned by + // XLOG2DEV must be negated if the layout direction is RTL. + const int xsrcOrig = GetLayoutDirection() == wxLayout_LeftToRight ? xsrc : -xsrc; const int ysrcOrig = ysrc; // This does the same thing as XLOG2DEV() but for the source DC. @@ -2366,10 +2368,13 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
// automatically as it doesn't even work with the source HDC. // So do this manually to ensure that the coordinates are // interpreted in the same way here as in all the other cases. - xsrc = source->LogicalToDeviceX(xsrcOrig); - ysrc = source->LogicalToDeviceY(ysrcOrig); - srcWidth = source->LogicalToDeviceXRel(srcWidth); - srcHeight = source->LogicalToDeviceYRel
(srcHeight); + const auto pt = source->LogicalToDevice(xsrcOrig, ysrcOrig); + const auto sz = source->LogicalToDeviceRel(srcWidth, srcHeight); + + xsrc = pt.x; + ysrc = pt.y; + srcWidth = sz.x; + srcHeight = sz.y;
// Figure out what co-ordinate system we're supposed to specify // ysrc in.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
There have been no replies to me previous comment but I still think we need to fix LogicalToDevice[XY]{Rel,}() instead of replacing them.
I realize now that m_signX should indeed be +1 even in RTL. But this just means that we should add an extra factor of -1 to these functions when RTL layout is used, shouldn't we?
Or, alternatively, just reimplement them in terms of natively implemented functions without X and Y in their name.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()