Fix clipping of scrolled windows under macOS Sonoma

We need to use a native clip view for things to behave correctly under
this OS version, otherwise scrollbars can be overdrawn by the window
contents.

Closes #24067.

Closes #24073.
This commit is contained in:
Stefan Csomor 2023-11-20 10:07:25 +01:00 committed by Vadim Zeitlin
parent 8ca312b17c
commit bcbc31e97f
5 changed files with 98 additions and 4 deletions

View file

@ -220,9 +220,14 @@ public :
// from the same pimpl class. // from the same pimpl class.
virtual void controlTextDidChange(); virtual void controlTextDidChange();
virtual void AdjustClippingView(wxScrollBar* horizontal, wxScrollBar* vertical) override;
virtual void UseClippingView(bool clip) override;
virtual WXWidget GetContainer() const override { return m_osxClipView ? m_osxClipView : m_osxView; }
protected: protected:
WXWidget m_osxView; WXWidget m_osxView;
WXWidget m_osxClipView;
// begins processing of native key down event, storing the native event for later wx event generation // begins processing of native key down event, storing the native event for later wx event generation
void BeginNativeKeyDownEvent( NSEvent* event ); void BeginNativeKeyDownEvent( NSEvent* event );
// done with the current native key down event // done with the current native key down event

View file

@ -365,6 +365,14 @@ public :
virtual bool EnableTouchEvents(int eventsMask) = 0; virtual bool EnableTouchEvents(int eventsMask) = 0;
// scrolling views need a clip subview that acts as parent for native children
// (except for the scollbars) which are children of the view itself
virtual void AdjustClippingView(wxScrollBar* horizontal, wxScrollBar* vertical);
virtual void UseClippingView(bool clip);
// returns native view which acts as a parent for native children
virtual WXWidget GetContainer() const;
// Mechanism used to keep track of whether a change should send an event // Mechanism used to keep track of whether a change should send an event
// Do SendEvents(false) when starting actions that would trigger programmatic events // Do SendEvents(false) when starting actions that would trigger programmatic events
// and SendEvents(true) at the end of the block. // and SendEvents(true) at the end of the block.

View file

@ -224,7 +224,7 @@ public:
// returns true if children have to clipped to the content area // returns true if children have to clipped to the content area
// (e.g., scrolled windows) // (e.g., scrolled windows)
bool MacClipChildren() const { return m_clipChildren ; } bool MacClipChildren() const { return m_clipChildren ; }
void MacSetClipChildren( bool clip ) { m_clipChildren = clip ; } void MacSetClipChildren( bool clip );
// returns true if the grandchildren need to be clipped to the children's content area // returns true if the grandchildren need to be clipped to the children's content area
// (e.g., splitter windows) // (e.g., splitter windows)

View file

@ -16,6 +16,7 @@
#include "wx/textctrl.h" #include "wx/textctrl.h"
#include "wx/combobox.h" #include "wx/combobox.h"
#include "wx/radiobut.h" #include "wx/radiobut.h"
#include "wx/scrolbar.h"
#endif #endif
#ifdef __WXMAC__ #ifdef __WXMAC__
@ -2562,7 +2563,8 @@ wxWidgetImpl( peer, flags )
{ {
Init(); Init();
m_osxView = w; m_osxView = w;
m_osxClipView = nil;
// check if the user wants to create the control initially hidden // check if the user wants to create the control initially hidden
if ( !peer->IsShown() ) if ( !peer->IsShown() )
SetVisibility(false); SetVisibility(false);
@ -3215,6 +3217,21 @@ bool wxWidgetCocoaImpl::CanFocus() const
return canFocus; return canFocus;
} }
@interface wxNSClipView : NSClipView
@end
@implementation wxNSClipView
#if wxOSX_USE_NATIVE_FLIPPED
- (BOOL)isFlipped
{
return YES;
}
#endif
@end
bool wxWidgetCocoaImpl::HasFocus() const bool wxWidgetCocoaImpl::HasFocus() const
{ {
NSView* targetView = m_osxView; NSView* targetView = m_osxView;
@ -3305,7 +3322,13 @@ void wxWidgetCocoaImpl::RemoveFromParent()
void wxWidgetCocoaImpl::Embed( wxWidgetImpl *parent ) void wxWidgetCocoaImpl::Embed( wxWidgetImpl *parent )
{ {
NSView* container = parent->GetWXWidget() ; NSView* container = nil;
if ( m_wxPeer->MacIsWindowScrollbar( parent->GetWXPeer()))
container = parent->GetWXWidget();
else
container = parent->GetContainer();
wxASSERT_MSG( container != nullptr , wxT("No valid mac container control") ) ; wxASSERT_MSG( container != nullptr , wxT("No valid mac container control") ) ;
[container addSubview:m_osxView]; [container addSubview:m_osxView];
@ -4034,6 +4057,41 @@ void wxWidgetCocoaImpl::SetDrawingEnabled(bool enabled)
[[m_osxView window] disableFlushWindow]; [[m_osxView window] disableFlushWindow];
} }
} }
void wxWidgetCocoaImpl::AdjustClippingView(wxScrollBar* horizontal, wxScrollBar* vertical)
{
if( m_osxClipView )
{
NSRect bounds = m_osxView.bounds;
if( horizontal && horizontal->IsShown() )
{
int sz = horizontal->GetSize().y;
bounds.size.height -= sz;
}
if( vertical && vertical->IsShown() )
{
int sz = vertical->GetSize().x;
bounds.size.width -= sz;
}
m_osxClipView.frame = bounds;
}
}
void wxWidgetCocoaImpl::UseClippingView(bool clip)
{
wxWindow* peer = m_wxPeer;
if ( peer && m_osxClipView == nil)
{
m_osxClipView = [[wxNSClipView alloc] initWithFrame: m_osxView.bounds];
[(NSClipView*)m_osxClipView setDrawsBackground: NO];
[m_osxView addSubview:m_osxClipView];
// TODO check for additional subwindows which might have to be moved to the clip view ?
}
}
// //
// Factory methods // Factory methods
// //

View file

@ -261,6 +261,13 @@ wxWindowMac::~wxWindowMac()
delete GetPeer() ; delete GetPeer() ;
} }
void wxWindowMac::MacSetClipChildren( bool clip )
{
m_clipChildren = clip ;
if ( m_peer )
m_peer->UseClippingView(clip);
}
WXWidget wxWindowMac::GetHandle() const WXWidget wxWindowMac::GetHandle() const
{ {
if ( GetPeer() ) if ( GetPeer() )
@ -386,6 +393,8 @@ bool wxWindowMac::Create(wxWindowMac *parent,
{ {
SetPeer(wxWidgetImpl::CreateUserPane( this, parent, id, pos, size , style, GetExtraStyle() )); SetPeer(wxWidgetImpl::CreateUserPane( this, parent, id, pos, size , style, GetExtraStyle() ));
MacPostControlCreate(pos, size) ; MacPostControlCreate(pos, size) ;
if ( m_clipChildren )
m_peer->UseClippingView(m_clipChildren);
} }
wxWindowCreateEvent event((wxWindow*)this); wxWindowCreateEvent event((wxWindow*)this);
@ -2139,6 +2148,7 @@ void wxWindowMac::MacRepositionScrollBars()
m_growBox->Hide(); m_growBox->Hide();
} }
} }
m_peer->AdjustClippingView(m_hScrollBar, m_vScrollBar);
#endif #endif
} }
@ -2700,3 +2710,16 @@ bool wxWidgetImpl::NeedsFrame() const
void wxWidgetImpl::SetDrawingEnabled(bool WXUNUSED(enabled)) void wxWidgetImpl::SetDrawingEnabled(bool WXUNUSED(enabled))
{ {
} }
void wxWidgetImpl::AdjustClippingView(wxScrollBar* WXUNUSED(horizontal), wxScrollBar* WXUNUSED(vertical))
{
}
void wxWidgetImpl::UseClippingView(bool WXUNUSED(clip))
{
}
WXWidget wxWidgetImpl::GetContainer() const
{
return GetWXWidget();
}