tdf#155092 use tracking areas instead of tracking rectangles

Apparently, the older, tracking rectangles selectors cause
unexpected window resizing upon the first mouse down after the
window has been manually resized so switch to the newer,
tracking areas selectors. Also, the NSTrackingInVisibleRect
option allows us to create one single tracking area that
resizes itself automatically over the lifetime of the view.

Note: for some unknown reason, both NSTrackingMouseMoved and
NSTrackingAssumeInside are necessary options for this fix
to work.

Change-Id: I1f771628d872166ff63a52990f671df053d144ca
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159845
Tested-by: Jenkins
Reviewed-by: Patrick Luby <plubius@neooffice.org>
diff --git a/vcl/inc/osx/salframe.h b/vcl/inc/osx/salframe.h
index 2d9b908..717e5f3 100644
--- a/vcl/inc/osx/salframe.h
+++ b/vcl/inc/osx/salframe.h
@@ -78,7 +78,6 @@ public:

    PointerStyle                    mePointerStyle;         // currently active pointer style

    NSTrackingRectTag               mnTrackingRectTag;      // used to get enter/leave messages
    NSRect                          maTrackingRect;

    CGMutablePathRef                mrClippingPath;         // used for "shaping"
diff --git a/vcl/inc/osx/salframeview.h b/vcl/inc/osx/salframeview.h
index f9eca27..1bc2b27 100644
--- a/vcl/inc/osx/salframeview.h
+++ b/vcl/inc/osx/salframeview.h
@@ -28,7 +28,6 @@ enum class SalEvent;
{
    AquaSalFrame*       mpFrame;
    id mDraggingDestinationHandler;
    BOOL                mbInLiveResize;
    BOOL                mbInWindowDidResize;
    NSTimer*            mpLiveResizeTimer;
}
@@ -262,6 +261,9 @@ enum class SalEvent;
-(NSArray *)accessibilityChildren;
-(NSArray <id<NSAccessibilityElement>> *)accessibilityChildrenInNavigationOrder;

-(void)viewWillStartLiveResize;
-(void)viewDidEndLiveResize;

@end

@interface SalFrameViewA11yWrapper : AquaA11yWrapper
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index a8616c4..2240de31 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -84,7 +84,6 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle 
    mpMenu( nullptr ),
    mnExtStyle( 0 ),
    mePointerStyle( PointerStyle::Arrow ),
    mnTrackingRectTag( 0 ),
    mrClippingPath( nullptr ),
    mnICOptions( InputContextFlags::NONE ),
    mnBlinkCursorDelay( nMinBlinkCursorDelay ),
@@ -237,14 +236,27 @@ void AquaSalFrame::initWindowAndView()
    if( mnStyle & SalFrameStyleFlags::TOOLTIP )
        [mpNSWindow setIgnoresMouseEvents: YES];
    else
        [mpNSWindow setAcceptsMouseMovedEvents: YES];
        // Related: tdf#155092 mouse events are now handled by tracking areas
        [mpNSWindow setAcceptsMouseMovedEvents: NO];
    [mpNSWindow setHasShadow: YES];

    [mpNSWindow setDelegate: static_cast<id<NSWindowDelegate> >(mpNSWindow)];

    [mpNSWindow setRestorable:NO];
    const NSRect aRect = { NSZeroPoint, NSMakeSize(maGeometry.width(), maGeometry.height()) };
    mnTrackingRectTag = [mpNSView addTrackingRect: aRect owner: mpNSView userData: nil assumeInside: NO];

    // tdf#155092 use tracking areas instead of tracking rectangles
    // Apparently, the older, tracking rectangles selectors cause
    // unexpected window resizing upon the first mouse down after the
    // window has been manually resized so switch to the newer,
    // tracking areas selectors. Also, the NSTrackingInVisibleRect
    // option allows us to create one single tracking area that
    // resizes itself automatically over the lifetime of the view.
    // Note: for some unknown reason, both NSTrackingMouseMoved and
    // NSTrackingAssumeInside are necessary options for this fix
    // to work.
    NSTrackingArea *pTrackingArea = [[NSTrackingArea alloc] initWithRect: [mpNSView bounds] options: ( NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingAssumeInside | NSTrackingInVisibleRect ) owner: mpNSView userInfo: nil];
    [mpNSView addTrackingArea: pTrackingArea];
    [pTrackingArea release];

    maSysData.mpNSView = mpNSView;

@@ -1832,7 +1844,6 @@ void AquaSalFrame::SetParent( SalFrame* pNewParent )

void AquaSalFrame::UpdateFrameGeometry()
{
    bool bFirstTime = (mnTrackingRectTag == 0);
    mbGeometryDidChange = false;

    if ( !mpNSWindow )
@@ -1848,7 +1859,7 @@ void AquaSalFrame::UpdateFrameGeometry()
    if( pScreen )
    {
        NSRect aNewScreenRect = [pScreen frame];
        if (bFirstTime || !NSEqualRects(maScreenRect, aNewScreenRect))
        if (!NSEqualRects(maScreenRect, aNewScreenRect))
        {
            mbGeometryDidChange = true;
            maScreenRect = aNewScreenRect;
@@ -1857,7 +1868,7 @@ void AquaSalFrame::UpdateFrameGeometry()
        if( pScreens )
        {
            unsigned int nNewDisplayScreenNumber = [pScreens indexOfObject: pScreen];
            if (bFirstTime || maGeometry.screen() != nNewDisplayScreenNumber)
            if (maGeometry.screen() != nNewDisplayScreenNumber)
            {
                mbGeometryDidChange = true;
                maGeometry.setScreen(nNewDisplayScreenNumber);
@@ -1870,22 +1881,17 @@ void AquaSalFrame::UpdateFrameGeometry()

    NSRect aTrackRect = { NSZeroPoint, aContentRect.size };

    if (bFirstTime || !NSEqualRects(maTrackingRect, aTrackRect))
    if (!NSEqualRects(maTrackingRect, aTrackRect))
    {
        mbGeometryDidChange = true;
        maTrackingRect = aTrackRect;

        // release old track rect
        [mpNSView removeTrackingRect: mnTrackingRectTag];
        // install the new track rect
        mnTrackingRectTag = [mpNSView addTrackingRect: aTrackRect owner: mpNSView userData: nil assumeInside: NO];
    }

    // convert to vcl convention
    CocoaToVCL( aFrameRect );
    CocoaToVCL( aContentRect );

    if (bFirstTime || !NSEqualRects(maContentRect, aContentRect) || !NSEqualRects(maFrameRect, aFrameRect))
    if (!NSEqualRects(maContentRect, aContentRect) || !NSEqualRects(maFrameRect, aFrameRect))
    {
        mbGeometryDidChange = true;

diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index 27c9e77..c583205 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -196,6 +196,26 @@ static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArra
    return pRet;
}

// Update ImplGetSVData()->mpWinData->mbIsLiveResize and return the old value
static bool updateWinDataInLiveResize(bool bInLiveResize)
{
    bool bRet = false;

    ImplSVData* pSVData = ImplGetSVData();
    assert( pSVData );
    if ( pSVData )
    {
        bRet = pSVData->mpWinData->mbIsLiveResize;
        if ( bRet != bInLiveResize )
        {
            pSVData->mpWinData->mbIsLiveResize = bInLiveResize;
            Scheduler::Wakeup();
        }
    }

    return bRet;
}

@interface NSResponder (SalFrameWindow)
-(BOOL)accessibilityIsIgnored;
@end
@@ -204,7 +224,6 @@ static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArra
-(id)initWithSalFrame: (AquaSalFrame*)pFrame
{
    mDraggingDestinationHandler = nil;
    mbInLiveResize = NO;
    mbInWindowDidResize = NO;
    mpLiveResizeTimer = nil;
    mpFrame = pFrame;
@@ -387,22 +406,9 @@ static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArra
        mpFrame->CallCallback( SalEvent::Resize, nullptr );

        bool bInLiveResize = [self inLiveResize];
        ImplSVData* pSVData = ImplGetSVData();
        assert( pSVData );
        if ( pSVData )
        bool bOldInLiveResize = updateWinDataInLiveResize(bInLiveResize);
        if ( bInLiveResize || bOldInLiveResize )
        {
            const bool bWasLiveResize = pSVData->mpWinData->mbIsLiveResize;
            if ( bWasLiveResize != bInLiveResize )
            {
                pSVData->mpWinData->mbIsLiveResize = bInLiveResize;
                Scheduler::Wakeup();
            }
        }

        if ( bInLiveResize || mbInLiveResize )
        {
            mbInLiveResize = bInLiveResize;

#if HAVE_FEATURE_SKIA
            // Related: tdf#152703 Eliminate empty window with Skia/Metal while resizing
            // The window will clear its background so when Skia/Metal is
@@ -441,7 +447,7 @@ static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArra
            [self setMinSize:aMinSize];
            [self setMaxSize:aMaxSize];

            if ( mbInLiveResize )
            if ( bInLiveResize )
            {
                // tdf#152703 Force repaint after live resizing ends
                // Repost this notification so that this selector will be called
@@ -2512,6 +2518,16 @@ static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArra
    return [self accessibilityChildren];
}

-(void)viewWillStartLiveResize
{
    updateWinDataInLiveResize(true);
}

-(void)viewDidEndLiveResize
{
    updateWinDataInLiveResize(false);
}

@end

@implementation SalFrameViewA11yWrapper
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index bf1b11a..3c98e4f 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -607,7 +607,9 @@ bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)

            [NSApp updateWindows];

            if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] )
            // Related: tdf#155092 keep LibreOffice state closely synched to
            // the dispatched native event state during live resize.
            if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] || ImplGetSVData()->mpWinData->mbIsLiveResize )
                break;
            // noelgrandin: I see sporadic hangs on the macos jenkins boxes, and the backtrace
            // points to the this loop - let us see if breaking out of here after too many
@@ -630,14 +632,16 @@ bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
        {
            SolarMutexReleaser aReleaser;

            // attempt to fix macos jenkins hangs - part 3
            // Related: tdf#155092 don't block during a live resize.
            // Also, attempt to fix macos jenkins hangs - part 3
            // oox::xls::WorkbookFragment::finalizeImport() calls
            // AquaSalInstance::DoYield() with bWait set to true. But
            // since unit tests generally have no expected user generated
            // events, we can end up blocking and waiting forever so
            // don't block and wait when running unit tests.
            NSDate *pDate = ( ImplGetSVData()->mpWinData->mbIsLiveResize || SalInstance::IsRunningUnitTest() ) ? [NSDate distantPast] : [NSDate distantFuture];
            pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
                            untilDate: SalInstance::IsRunningUnitTest() ? [NSDate distantPast] : [NSDate distantFuture]
                            untilDate: pDate
                            inMode: NSDefaultRunLoopMode
                            dequeue: YES];
            if( pEvent )