vcl/inc/osx/salframe.h  |   14 ++-
 vcl/osx/salframe.cxx    |  195 +++++++++++++++++++++++++++++++++++-------------
 vcl/osx/salframeview.mm |  144 ++++++++++++++++++++++++++++++++---
 vcl/osx/salinst.cxx     |    2 
 vcl/osx/salnsmenu.mm    |   15 +++
 5 files changed, 307 insertions(+), 63 deletions(-)

New commits:
commit f9919532d3d5a547196d79479c1e1c088d9a6943
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sat Dec 14 19:38:03 2024 -0500
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Wed Jan 1 08:07:19 2025 +0100

    tdf#161623 Handle windows that macOS forces to native full screen mode
    
    Starting with commit c1452e73091412ba0bb72306329e1912df2ba513, native
    full screen was disabled. However, in certain cases, macOS will force
    a window into native full screen mode and this caused numerous bugs.
    
    So add the following fixes to support native full screen windows. Note:
    the green titlebar button will still zoom the window. That button will
    only escape native full screen mode only when macOS has already forced
    a window into native full screen mode:
    
    - When in native full screen mode, -[NSWindow styleMask] will include
      NSWindowStyleMaskFullScreen which can affect the frame and content
      rectangle calculations so always use -[NSWindow styleMask] instead
      of the mask that was used to create the window when doing such
      calculations.
    
    - A comment in commit c1452e73091412ba0bb72306329e1912df2ba513 mentions
      crashing after ordering out a native full screen window. I have not
      experienced any crashing, but I did find that ordering out would
      leave the application in a state where there is no Desktop and both
      the menubar and the Dock are hidden. The fix, which is to close
      windows instead of ordering them out, was copied from the following
      NeoOffice source code file which is licensed under the Mozilla Public
      License, v. 2.0:
    
      
https://github.com/neooffice/NeoOffice/blob/NeoOffice-2022_7/vcl/java/source/window/salframe.mm
    
    - In AquaSalFrame::GetWindowState(), add both the original and the
      curent frame when the window is in LibreOffice and/or native full
      screen mode.
    
    - Track LibreOffice and native full screen state in separate instance
      variables so that both modes can be activated independently and the
      window is not set back to its original size unitl a window has exited
      both full screen modes.
    
    LibreOffice also has its own full screen mode which hides the menubar
    and Dock, resizes to fill the screen, and hides all of its toolbars.
    As much as possible, both full screen modes should coexist and the
    user can enter or exit LibreOffice full screen mode while in native
    full screen mode. So add the following fixes for LibreOffice full
    screen mode:
    
    - Do not add the window's titlebar height to the window's frame as
      that will cause the titlebar to be pushed offscreen.
    
    - The menubar and Dock are both hidden when a window enters LibreOffice
      full screen mode. However, -[NSWindow setFrame:display:] shrinks the
      window frame to allow room for the menubar if the window is on the
      main screen. So, force the return value to match the frame that
      LibreOffice expects.
    
    - Multiple windows can be in LibreOffice full screen mode at the
      same time so hide or show the menubar and Dock when a LibreOffice
      full screen window gains or loses focus.
    
    - When a window is in LibreOffice full screen mode, LibreOffice hides
      the menubar. However, when in native full screen mode, hiding the
      menubar causes the window's titlebar to either fail to display or
      fail to hide when expected. So allow the menubar to remain visible
      when a window is in both LibreOffice and native full screen mode
      and disable all LibreOffice menus (like is done when a modal windowj
      is displayed) to mimic the effect of hiding the menubar.
    
    Note: this change is a combination of the following commits:
    
    - Commit 13bdc2ba0cec0cb1c9e8077eb2217f69822a805f
    - Commit 05b508f9f1397249079842dca86c36cbca01b43d
    - Commit 2120acaa261aaa1adaafc051582ac7f7dd4de372
    
    Change-Id: I94ae24a03d192a681d5500930f7fec70e595ffaf
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178497
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>
    Tested-by: Jenkins
    (cherry picked from commit 13bdc2ba0cec0cb1c9e8077eb2217f69822a805f)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178820
    Reviewed-by: Christian Lohmaier <lohmaier+libreoff...@googlemail.com>
    (cherry picked from commit 1d524110b1863d448254529bd8911e061dac2eb0)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178821
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/vcl/inc/osx/salframe.h b/vcl/inc/osx/salframe.h
index 36121c437896..5842693d7759 100644
--- a/vcl/inc/osx/salframe.h
+++ b/vcl/inc/osx/salframe.h
@@ -58,9 +58,7 @@ public:
     int                             mnMinHeight;            // min. client 
height in pixels
     int                             mnMaxWidth;             // max. client 
width in pixels
     int                             mnMaxHeight;            // max. client 
height in pixels
-    NSRect                          maFullScreenRect;       // old window size 
when in FullScreen
     bool                            mbGraphics;             // is Graphics 
used?
-    bool                            mbFullScreen;           // is Window in 
FullScreen?
     bool                            mbShown;
     bool                            mbInitShow;
     bool                            mbPositioned;
@@ -102,6 +100,18 @@ public:
     // tdf#164428 force flush after drawing a progress bar
     bool                            mbForceFlushProgressBar;
 
+    // Is window in LibreOffice full screen mode
+    bool                            mbInternalFullScreen;
+    // Window size to restore to when exiting LibreOffice full screen mode
+    NSRect                          maInternalFullScreenRestoreRect;
+    // Desired window size when entering exiting LibreOffice full screen mode
+    NSRect                          maInternalFullScreenExpectedRect;
+
+    // Is window in native full screen mode
+    bool                            mbNativeFullScreen;
+    // Window size to restore to when exiting LibreOffice full screen mode
+    NSRect                          maNativeFullScreenRestoreRect;
+
 public:
     /** Constructor
 
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index 75f27080121c..70ee6e175d9a 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -74,7 +74,6 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, 
SalFrameStyleFlags salFrameStyle
     mnMaxWidth(0),
     mnMaxHeight(0),
     mbGraphics(false),
-    mbFullScreen( false ),
     mbShown(false),
     mbInitShow(true),
     mbPositioned(false),
@@ -91,7 +90,12 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, 
SalFrameStyleFlags salFrameStyle
     mnICOptions( InputContextFlags::NONE ),
     mnBlinkCursorDelay( nMinBlinkCursorDelay ),
     mbForceFlushScrolling( false ),
-    mbForceFlushProgressBar( false )
+    mbForceFlushProgressBar( false ),
+    mbInternalFullScreen( false ),
+    maInternalFullScreenRestoreRect( NSZeroRect ),
+    maInternalFullScreenExpectedRect( NSZeroRect ),
+    mbNativeFullScreen( false ),
+    maNativeFullScreenRestoreRect( NSZeroRect )
 {
     mpParent = dynamic_cast<AquaSalFrame*>(pParent);
 
@@ -123,7 +127,7 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, 
SalFrameStyleFlags salFrameStyle
 
 AquaSalFrame::~AquaSalFrame()
 {
-    if (mbFullScreen)
+    if (mbInternalFullScreen)
         doShowFullScreen(false, maGeometry.screen());
 
     assert( GetSalData()->mpInstance->IsMainThread() );
@@ -403,7 +407,7 @@ void AquaSalFrame::initShow()
     OSX_SALDATA_RUNINMAIN( initShow() )
 
     mbInitShow = false;
-    if( ! mbPositioned && ! mbFullScreen )
+    if( ! mbPositioned && ! mbInternalFullScreen )
     {
         AbsoluteScreenPixelRectangle aScreenRect;
         GetWorkArea( aScreenRect );
@@ -544,7 +548,11 @@ void AquaSalFrame::Show(bool bVisible, bool bNoActivate)
         if( mpParent && [mpNSWindow parentWindow] == mpParent->mpNSWindow )
             [mpParent->mpNSWindow removeChildWindow: mpNSWindow];
 
-        [mpNSWindow orderOut: NSApp];
+        // Related: tdf#161623 close windows, don't order them out
+        // Ordering out a native full screen window would leave the
+        // application in a state where there is no Desktop and both
+        // the menubar and the Dock are hidden.
+        [mpNSWindow close];
     }
 }
 
@@ -675,7 +683,7 @@ void AquaSalFrame::SetWindowState(const vcl::WindowData* 
pState)
 
     // set normal state
     NSRect aStateRect = [mpNSWindow frame];
-    aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: 
mnStyleMask];
+    aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: 
[mpNSWindow styleMask]];
     CocoaToVCL(aStateRect);
     if (pState->mask() & vcl::WindowDataMask::X)
         aStateRect.origin.x = float(pState->x());
@@ -686,7 +694,7 @@ void AquaSalFrame::SetWindowState(const vcl::WindowData* 
pState)
     if (pState->mask() & vcl::WindowDataMask::Height)
         aStateRect.size.height = float(pState->height());
     VCLToCocoa(aStateRect);
-    aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: 
mnStyleMask];
+    aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: 
[mpNSWindow styleMask]];
     [mpNSWindow setFrame: aStateRect display: NO];
 
     if (pState->state() == vcl::WindowState::Minimized)
@@ -745,19 +753,53 @@ bool AquaSalFrame::GetWindowState(vcl::WindowData* pState)
     pState->setMask(vcl::WindowDataMask::PosSizeState);
 
     NSRect aStateRect = [mpNSWindow frame];
-    aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: 
mnStyleMask];
+    aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: 
[mpNSWindow styleMask]];
     CocoaToVCL( aStateRect );
-    pState->setX(static_cast<sal_Int32>(aStateRect.origin.x));
-    pState->setY(static_cast<sal_Int32>(aStateRect.origin.y));
-    pState->setWidth(static_cast<sal_uInt32>(aStateRect.size.width));
-    pState->setHeight(static_cast<sal_uInt32>(aStateRect.size.height));
 
-    if( [mpNSWindow isMiniaturized] )
-        pState->setState(vcl::WindowState::Minimized);
-    else if( ! [mpNSWindow isZoomed] )
-        pState->setState(vcl::WindowState::Normal);
+    if( mbInternalFullScreen && !NSIsEmptyRect( 
maInternalFullScreenRestoreRect ) )
+    {
+        pState->setX(maInternalFullScreenRestoreRect.origin.x);
+        pState->setY(maInternalFullScreenRestoreRect.origin.y);
+        pState->setWidth(maInternalFullScreenRestoreRect.size.width);
+        pState->setHeight(maInternalFullScreenRestoreRect.size.height);
+        pState->SetMaximizedX(static_cast<sal_Int32>(aStateRect.origin.x));
+        pState->SetMaximizedY(static_cast<sal_Int32>(aStateRect.origin.x));
+        
pState->SetMaximizedWidth(static_cast<sal_uInt32>(aStateRect.size.width));
+        
pState->SetMaximizedHeight(static_cast<sal_uInt32>(aStateRect.size.height));
+
+        pState->rMask() |= vcl::WindowDataMask::MaximizedX | 
vcl::WindowDataMask::MaximizedY | vcl::WindowDataMask::MaximizedWidth | 
vcl::WindowDataMask::MaximizedHeight;
+
+        pState->setState(vcl::WindowState::FullScreen);
+    }
+    else if( mbNativeFullScreen && !NSIsEmptyRect( 
maNativeFullScreenRestoreRect ) )
+    {
+        pState->setX(maNativeFullScreenRestoreRect.origin.x);
+        pState->setY(maNativeFullScreenRestoreRect.origin.y);
+        pState->setWidth(maNativeFullScreenRestoreRect.size.width);
+        pState->setHeight(maNativeFullScreenRestoreRect.size.height);
+        pState->SetMaximizedX(static_cast<sal_Int32>(aStateRect.origin.x));
+        pState->SetMaximizedY(static_cast<sal_Int32>(aStateRect.origin.x));
+        
pState->SetMaximizedWidth(static_cast<sal_uInt32>(aStateRect.size.width));
+        
pState->SetMaximizedHeight(static_cast<sal_uInt32>(aStateRect.size.height));
+
+        pState->rMask() |= vcl::WindowDataMask::MaximizedX | 
vcl::WindowDataMask::MaximizedY | vcl::WindowDataMask::MaximizedWidth | 
vcl::WindowDataMask::MaximizedHeight;
+
+        pState->setState(vcl::WindowState::FullScreen);
+    }
     else
-        pState->setState(vcl::WindowState::Maximized);
+    {
+        pState->setX(static_cast<sal_Int32>(aStateRect.origin.x));
+        pState->setY(static_cast<sal_Int32>(aStateRect.origin.y));
+        pState->setWidth(static_cast<sal_uInt32>(aStateRect.size.width));
+        pState->setHeight(static_cast<sal_uInt32>(aStateRect.size.height));
+
+        if( [mpNSWindow isMiniaturized] )
+            pState->setState(vcl::WindowState::Minimized);
+        else if( ! [mpNSWindow isZoomed] )
+            pState->setState(vcl::WindowState::Normal);
+        else
+            pState->setState(vcl::WindowState::Maximized);
+    }
 
     return true;
 }
@@ -813,22 +855,18 @@ void AquaSalFrame::doShowFullScreen( bool bFullScreen, 
sal_Int32 nDisplay )
         return;
     }
 
-    SAL_INFO("vcl.osx", __func__ << ": mbFullScreen=" << mbFullScreen << ", 
bFullScreen=" << bFullScreen);
+    SAL_INFO("vcl.osx", __func__ << ": mbInternalFullScreen=" << 
mbInternalFullScreen << ", bFullScreen=" << bFullScreen);
 
-    if( mbFullScreen == bFullScreen )
+    if( mbInternalFullScreen == bFullScreen )
         return;
 
     OSX_SALDATA_RUNINMAIN( ShowFullScreen( bFullScreen, nDisplay ) )
 
-    mbFullScreen = bFullScreen;
+    mbInternalFullScreen = bFullScreen;
 
     if( bFullScreen )
     {
-        // hide the dock and the menubar if we are on the menu screen
-        // which is always on index 0 according to documentation
-        bool bHideMenu = (nDisplay == 0);
-
-        NSRect aNewContentRect = NSZeroRect;
+        NSRect aNewFrameRect = NSZeroRect;
         // get correct screen
         NSScreen* pScreen = nil;
         NSArray* pScreens = [NSScreen screens];
@@ -839,51 +877,110 @@ void AquaSalFrame::doShowFullScreen( bool bFullScreen, 
sal_Int32 nDisplay )
             else
             {
                 // this means span all screens
-                bHideMenu = true;
                 NSEnumerator* pEnum = [pScreens objectEnumerator];
                 while( (pScreen = [pEnum nextObject]) != nil )
                 {
                     NSRect aScreenRect = [pScreen frame];
-                    if( aScreenRect.origin.x < aNewContentRect.origin.x )
+                    if( aScreenRect.origin.x < aNewFrameRect.origin.x )
                     {
-                        aNewContentRect.size.width += aNewContentRect.origin.x 
- aScreenRect.origin.x;
-                        aNewContentRect.origin.x = aScreenRect.origin.x;
+                        aNewFrameRect.size.width += aNewFrameRect.origin.x - 
aScreenRect.origin.x;
+                        aNewFrameRect.origin.x = aScreenRect.origin.x;
                     }
-                    if( aScreenRect.origin.y < aNewContentRect.origin.y )
+                    if( aScreenRect.origin.y < aNewFrameRect.origin.y )
                     {
-                        aNewContentRect.size.height += 
aNewContentRect.origin.y - aScreenRect.origin.y;
-                        aNewContentRect.origin.y = aScreenRect.origin.y;
+                        aNewFrameRect.size.height += aNewFrameRect.origin.y - 
aScreenRect.origin.y;
+                        aNewFrameRect.origin.y = aScreenRect.origin.y;
                     }
-                    if( aScreenRect.origin.x + aScreenRect.size.width > 
aNewContentRect.origin.x + aNewContentRect.size.width )
-                        aNewContentRect.size.width = aScreenRect.origin.x + 
aScreenRect.size.width - aNewContentRect.origin.x;
-                    if( aScreenRect.origin.y + aScreenRect.size.height > 
aNewContentRect.origin.y + aNewContentRect.size.height )
-                        aNewContentRect.size.height = aScreenRect.origin.y + 
aScreenRect.size.height - aNewContentRect.origin.y;
+                    if( aScreenRect.origin.x + aScreenRect.size.width > 
aNewFrameRect.origin.x + aNewFrameRect.size.width )
+                        aNewFrameRect.size.width = aScreenRect.origin.x + 
aScreenRect.size.width - aNewFrameRect.origin.x;
+                    if( aScreenRect.origin.y + aScreenRect.size.height > 
aNewFrameRect.origin.y + aNewFrameRect.size.height )
+                        aNewFrameRect.size.height = aScreenRect.origin.y + 
aScreenRect.size.height - aNewFrameRect.origin.y;
                 }
             }
         }
-        if( aNewContentRect.size.width == 0 && aNewContentRect.size.height == 
0 )
+        if( aNewFrameRect.size.width == 0 && aNewFrameRect.size.height == 0 )
         {
             if( pScreen == nil )
                 pScreen = [mpNSWindow screen];
             if( pScreen == nil )
                 pScreen = [NSScreen mainScreen];
 
-            aNewContentRect = [pScreen frame];
+            aNewFrameRect = [pScreen frame];
+        }
+
+        // Show the menubar if application is in native full screen mode
+        // since hiding the menubar in that mode will cause the window's
+        // titlebar to fail to display or hide as expected.
+        if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
+        {
+            [NSMenu setMenuBarVisible: YES];
         }
+        // Hide the dock and the menubar if this or one of its child
+        // windows are the key window
+        else if( AquaSalFrame::isAlive( this ) )
+        {
+            bool bNativeFullScreen = false;
+            const AquaSalFrame *pParentFrame = this;
+            while( pParentFrame )
+            {
+                bNativeFullScreen |= pParentFrame->mbNativeFullScreen;
+                pParentFrame = AquaSalFrame::isAlive( pParentFrame->mpParent ) 
? pParentFrame->mpParent : nullptr;
+            }
 
-        if( bHideMenu )
-            [NSMenu setMenuBarVisible:NO];
+            if( !bNativeFullScreen )
+            {
+                const NSWindow *pParentWindow = [NSApp keyWindow];
+                while( pParentWindow && pParentWindow != mpNSWindow )
+                    pParentWindow = [pParentWindow parentWindow];
+                if( pParentWindow == mpNSWindow )
+                    [NSMenu setMenuBarVisible: NO];
+            }
+        }
+
+        if( mbNativeFullScreen && !NSIsEmptyRect( 
maNativeFullScreenRestoreRect ) )
+            maInternalFullScreenRestoreRect = maNativeFullScreenRestoreRect;
+        else
+            maInternalFullScreenRestoreRect = [mpNSWindow frame];
 
-        maFullScreenRect = [mpNSWindow frame];
+        // Related: tdf#161623 do not add the window's titlebar height
+        // to the window's frame as that will cause the titlebar to be
+        // pushed offscreen.
+        maInternalFullScreenExpectedRect = aNewFrameRect;
 
-        [mpNSWindow setFrame: [NSWindow frameRectForContentRect: 
aNewContentRect styleMask: mnStyleMask] display: mbShown ? YES : NO];
+        [mpNSWindow setFrame: maInternalFullScreenExpectedRect display: 
mbShown ? YES : NO];
     }
     else
     {
-        [mpNSWindow setFrame: maFullScreenRect display: mbShown ? YES : NO];
+        // Show the dock and the menubar if this or one of its children are
+        // the key window
+        const NSWindow *pParentWindow = [NSApp keyWindow];
+        while( pParentWindow && pParentWindow != mpNSWindow )
+            pParentWindow = [pParentWindow parentWindow];
+        if( pParentWindow == mpNSWindow )
+        {
+            [NSMenu setMenuBarVisible: YES];
+        }
+        // Show the dock and the menubar if there is no native modal dialog
+        // and if the key window is nil or is not a SalFrameWindow instance.
+        // If a SalFrameWindow is the key window, it should have already set
+        // the menubar visibility to match its LibreOffice full screen mode
+        // state.
+        else if( ![NSApp modalWindow] )
+        {
+            NSWindow *pKeyWindow = [NSApp keyWindow];
+            if( !pKeyWindow || ![pKeyWindow isKindOfClass: [SalFrameWindow 
class]] )
+                [NSMenu setMenuBarVisible: YES];
+        }
+
+        if( !NSIsEmptyRect( maInternalFullScreenRestoreRect ) )
+        {
+            if( !mbNativeFullScreen || NSIsEmptyRect( 
maNativeFullScreenRestoreRect ) )
+                [mpNSWindow setFrame: maInternalFullScreenRestoreRect display: 
mbShown ? YES : NO];
+
+            maInternalFullScreenRestoreRect = NSZeroRect;
+        }
 
-        // show the dock and the menubar
-        [NSMenu setMenuBarVisible:YES];
+        maInternalFullScreenExpectedRect = NSZeroRect;
     }
 
     UpdateFrameGeometry();
@@ -1610,7 +1707,7 @@ void AquaSalFrame::SetPosSize(
         [mpNSWindow deminiaturize: NSApp]; // expand the window
 
     NSRect aFrameRect = [mpNSWindow frame];
-    NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect 
styleMask: mnStyleMask];
+    NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect 
styleMask: [mpNSWindow styleMask]];
 
     // position is always relative to parent frame
     NSRect aParentContentRect;
@@ -1625,7 +1722,7 @@ void AquaSalFrame::SetPosSize(
                 nX = static_cast<tools::Long>(mpParent->maGeometry.width()) - 
aContentRect.size.width - 1 - nX;
         }
         NSRect aParentFrameRect = [mpParent->mpNSWindow frame];
-        aParentContentRect = [NSWindow contentRectForFrameRect: 
aParentFrameRect styleMask: mpParent->mnStyleMask];
+        aParentContentRect = [NSWindow contentRectForFrameRect: 
aParentFrameRect styleMask: [mpParent->mpNSWindow styleMask]];
     }
     else
         aParentContentRect = maScreenRect; // use screen if no parent
@@ -1656,7 +1753,7 @@ void AquaSalFrame::SetPosSize(
 
     // do not display yet, we need to update our backbuffer
     {
-        [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aContentRect 
styleMask: mnStyleMask] display: NO];
+        [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aContentRect 
styleMask: [mpNSWindow styleMask]] display: NO];
     }
 
     UpdateFrameGeometry();
@@ -1901,7 +1998,7 @@ void AquaSalFrame::UpdateFrameGeometry()
     }
 
     NSRect aFrameRect = [mpNSWindow frame];
-    NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect 
styleMask: mnStyleMask];
+    NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect 
styleMask: [mpNSWindow styleMask]];
 
     NSRect aTrackRect = { NSZeroPoint, aContentRect.size };
 
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index 47e01a3ce857..a34a57c7dc53 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -243,6 +243,47 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
     return ( pEvent && [pEvent type] == NSEventTypeScrollWheel && [pEvent 
phase] == NSEventPhaseNone && [pEvent momentumPhase] == NSEventPhaseNone );
 }
 
+static void updateMenuBarVisibility( const AquaSalFrame *pFrame )
+{
+    // Show the menubar if application is in native full screen mode
+    // since hiding the menubar in that mode will cause the window's
+    // titlebar to fail to display or fail to hide when expected.
+    if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
+    {
+        [NSMenu setMenuBarVisible: YES];
+    }
+    // Hide the dock and the menubar if the key window or one of its
+    // parent windows are in LibreOffice full screen mode. Otherwise,
+    // show the dock and the menubar.
+    else if( AquaSalFrame::isAlive( pFrame ) )
+    {
+        bool bInternalFullScreen = false;
+        bool bNativeFullScreen = false;
+        const AquaSalFrame *pParentFrame = pFrame;
+        while( pParentFrame )
+        {
+            bInternalFullScreen |= pParentFrame->mbInternalFullScreen;
+            bNativeFullScreen |= pParentFrame->mbNativeFullScreen;
+            pParentFrame = AquaSalFrame::isAlive( pParentFrame->mpParent ) ? 
pParentFrame->mpParent : nullptr;
+        }
+
+        if( bInternalFullScreen && !bNativeFullScreen )
+        {
+            const NSWindow *pParentWindow = [NSApp keyWindow];
+            while( pParentWindow && pParentWindow != pFrame->getNSWindow() )
+                pParentWindow = [pParentWindow parentWindow];
+            if( pParentWindow == pFrame->getNSWindow() )
+                [NSMenu setMenuBarVisible: NO];
+            else
+                [NSMenu setMenuBarVisible: YES];
+        }
+        else
+        {
+            [NSMenu setMenuBarVisible: YES];
+        }
+    }
+}
+
 @interface NSResponder (SalFrameWindow)
 -(BOOL)accessibilityIsIgnored;
 @end
@@ -281,6 +322,7 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
     // i.e. it maximizes / unmaximises the window. Sure, that state can also 
be confused with LO's
     // home-grown full-screen mode. Oh well.
 
+    [pNSWindow setReleasedWhenClosed: NO];
     [pNSWindow setCollectionBehavior: 
NSWindowCollectionBehaviorFullScreenNone];
 
     // Disable window restoration until we support it directly
@@ -354,7 +396,7 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
         return YES;
     if( mpFrame->mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
         return YES;
-    if( mpFrame->mbFullScreen )
+    if( mpFrame->mbInternalFullScreen )
         return YES;
     return [super canBecomeKeyWindow];
 }
@@ -380,12 +422,14 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
             mpFrame->mpMenu->setMainMenu();
         else if( ! mpFrame->mpParent &&
                  ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // 
set default menu for e.g. help
-                    mpFrame->mbFullScreen ) )                               // 
set default menu for e.g. presentation
+                    mpFrame->mbInternalFullScreen ) )                          
     // set default menu for e.g. presentation
         {
             AquaSalMenu::setDefaultMenu();
         }
         mpFrame->CallCallback( SalEvent::GetFocus, nullptr );
         mpFrame->SendPaintEvent(); // repaint controls as active
+
+        updateMenuBarVisibility( mpFrame );
     }
 
     // Prevent the same native input method popup that was cancelled in a
@@ -407,6 +451,25 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
         mpFrame->CallCallback(SalEvent::LoseFocus, nullptr);
         mpFrame->SendPaintEvent(); // repaint controls as inactive
     }
+
+    // Show the menubar if application is in native full screen mode
+    // since hiding the menubar in that mode will cause the window's
+    // titlebar to fail to display or fail to hide when expected.
+    if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
+    {
+        [NSMenu setMenuBarVisible: YES];
+    }
+    // Show the dock and the menubar if there is no native modal dialog
+    // and if the key window is nil or is not a SalFrameWindow instance.
+    // If a SalFrameWindow is the key window, it should have already set
+    // the menubar visibility to match its LibreOffice full screen mode
+    // state.
+    else if ( ![NSApp modalWindow] )
+    {
+        NSWindow *pKeyWindow = [NSApp keyWindow];
+        if( !pKeyWindow || ![pKeyWindow isKindOfClass: [SalFrameWindow class]] 
)
+            [NSMenu setMenuBarVisible: YES];
+    }
 }
 
 -(void)windowDidChangeScreen: (NSNotification*)pNotification
@@ -589,24 +652,58 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
     return bRet;
 }
 
--(void)windowDidEnterFullScreen: (NSNotification*)pNotification
+-(void)windowWillEnterFullScreen: (NSNotification*)pNotification
 {
+    (void)pNotification;
     SolarMutexGuard aGuard;
 
-    if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
-        return;
-    mpFrame->mbFullScreen = true;
-    (void)pNotification;
+    if( AquaSalFrame::isAlive( mpFrame) )
+    {
+        mpFrame->mbNativeFullScreen = true;
+
+        if( mpFrame->mbInternalFullScreen && !NSIsEmptyRect( 
mpFrame->maInternalFullScreenRestoreRect ) )
+            mpFrame->maNativeFullScreenRestoreRect = 
mpFrame->maInternalFullScreenRestoreRect;
+        else
+            mpFrame->maNativeFullScreenRestoreRect = [mpFrame->getNSWindow() 
frame];
+
+        updateMenuBarVisibility( mpFrame );
+    }
 }
 
--(void)windowDidExitFullScreen: (NSNotification*)pNotification
+-(void)windowDidFailToEnterFullScreen: (NSWindow *)pWindow
 {
+    (void)pWindow;
     SolarMutexGuard aGuard;
 
-    if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
-        return;
-    mpFrame->mbFullScreen = false;
+    if( AquaSalFrame::isAlive( mpFrame) )
+    {
+        mpFrame->mbNativeFullScreen = false;
+
+        mpFrame->maNativeFullScreenRestoreRect = NSZeroRect;
+
+        updateMenuBarVisibility( mpFrame );
+    }
+}
+
+-(void)windowDidExitFullScreen: (NSNotification*)pNotification
+{
     (void)pNotification;
+    SolarMutexGuard aGuard;
+
+    if( AquaSalFrame::isAlive( mpFrame) )
+    {
+        mpFrame->mbNativeFullScreen = false;
+
+        if( !NSIsEmptyRect( mpFrame->maNativeFullScreenRestoreRect ) )
+        {
+            if ( !mpFrame->mbInternalFullScreen || NSIsEmptyRect( 
mpFrame->maInternalFullScreenRestoreRect ) )
+                [mpFrame->getNSWindow() setFrame: 
mpFrame->maNativeFullScreenRestoreRect display: mpFrame->mbShown ? YES : NO];
+
+            mpFrame->maNativeFullScreenRestoreRect = NSZeroRect;
+        }
+
+        updateMenuBarVisibility( mpFrame );
+    }
 }
 
 -(void)windowDidChangeBackingProperties:(NSNotification *)pNotification
@@ -784,6 +881,31 @@ static bool isMouseScrollWheelEvent( NSEvent *pEvent )
     [self clearResetParentWindowTimer];
 }
 
+-(NSRect)constrainFrameRect: (NSRect)aFrameRect toScreen: (NSScreen *)pScreen
+{
+    SolarMutexGuard aGuard;
+
+    NSRect aRet = [super constrainFrameRect: aFrameRect toScreen: pScreen];
+
+    // Related: tdf#161623 the menubar and Dock are both hidden when a
+    // window enters LibreOffice full screen mode. However, the call to
+    // -[super constrainFrameRect:toScreen:] shrinks the window frame to
+    // allow room for the menubar if the window is on the main screen. So,
+    // force the return value to match the frame that LibreOffice expects.
+    if( AquaSalFrame::isAlive( mpFrame) && mpFrame->mbInternalFullScreen && 
!NSIsEmptyRect( mpFrame->maInternalFullScreenExpectedRect ) )
+        aRet = mpFrame->maInternalFullScreenExpectedRect;
+
+    return aRet;
+}
+
+- (NSArray<NSWindow *> *)customWindowsToExitFullScreenForWindow: (NSWindow 
*)pWindow
+{
+    // Related: tdf#161623 our code will reset the frame immediately after
+    // native full screen mode has exited so suppress animation when exiting
+    // native full screen mode.
+    return [NSArray arrayWithObject: self];
+}
+
 @end
 
 @implementation SalFrameView
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index 42a8efe029e4..87263092b7f7 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -468,7 +468,7 @@ void AquaSalInstance::handleAppDefinedEvent( NSEvent* 
pEvent )
         for( auto pSalFrame : pInst->getFrames() )
         {
             const AquaSalFrame* pFrame = static_cast<const AquaSalFrame*>( 
pSalFrame );
-            if ( pFrame->mbFullScreen )
+            if ( pFrame->mbInternalFullScreen )
             {
                 bIsFullScreenMode = true;
                 break;
diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm
index 383e0b574a54..ff1dca86b1b9 100644
--- a/vcl/osx/salnsmenu.mm
+++ b/vcl/osx/salnsmenu.mm
@@ -284,6 +284,21 @@
     if (!pMenuItem || [NSApp modalWindow])
         return NO;
 
+    // Related: tdf#161623 the menubar is always visible when in native
+    // full screen mode so disable all menu items when also in LibreOffice
+    // full screen mode to mimic the effect of a hidden menubar.
+    SolarMutexGuard aGuard;
+    const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? 
mpMenuItem->mpParentMenu->getFrame() : nullptr;
+    if (pFrame && AquaSalFrame::isAlive( pFrame ) && 
pFrame->mbInternalFullScreen)
+    {
+        NSMenu *pMainMenu = [NSApp mainMenu];
+        NSMenu *pParentMenu = [pMenuItem menu];
+        while (pParentMenu && pParentMenu != pMainMenu)
+            pParentMenu = [pParentMenu supermenu];
+        if (pParentMenu && pParentMenu == pMainMenu)
+            return NO;
+    }
+
     // Related: tdf#126638 return the last enabled state set by the 
LibreOffice code
     // Apparently whatever is returned will be passed to
     // -[NSMenuItem setEnabled:] which can cause the enabled state

Reply via email to