diff options
author | Patrick Luby <guibmacdev@gmail.com> | 2024-12-14 19:38:03 -0500 |
---|---|---|
committer | Christian Lohmaier <lohmaier+LibreOffice@googlemail.com> | 2024-12-19 23:19:00 +0100 |
commit | 1d524110b1863d448254529bd8911e061dac2eb0 (patch) | |
tree | 98b51e32b970fd9bcd87e043298b2213e2dab0d5 | |
parent | 6966db05a83dc1e7ca2768d811776fcace50ecc3 (diff) |
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 <guibomacdev@gmail.com>
Tested-by: Jenkins
(cherry picked from commit 13bdc2ba0cec0cb1c9e8077eb2217f69822a805f)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178820
Reviewed-by: Christian Lohmaier <lohmaier+LibreOffice@googlemail.com>
-rw-r--r-- | vcl/inc/osx/salframe.h | 14 | ||||
-rw-r--r-- | vcl/osx/salframe.cxx | 195 | ||||
-rw-r--r-- | vcl/osx/salframeview.mm | 144 | ||||
-rw-r--r-- | vcl/osx/salinst.cxx | 2 | ||||
-rw-r--r-- | vcl/osx/salnsmenu.mm | 15 |
5 files changed, 307 insertions, 63 deletions
diff --git a/vcl/inc/osx/salframe.h b/vcl/inc/osx/salframe.h index 7245a89c3447..fa2a5bcb3cbb 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; @@ -100,6 +98,18 @@ public: // tdf#155266 force flush after scrolling bool mbForceFlush; + // 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 9d85969363b6..98ea5a72c718 100644 --- a/vcl/osx/salframe.cxx +++ b/vcl/osx/salframe.cxx @@ -76,7 +76,6 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle mnMaxWidth(0), mnMaxHeight(0), mbGraphics(false), - mbFullScreen( false ), mbShown(false), mbInitShow(true), mbPositioned(false), @@ -92,7 +91,12 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle mrClippingPath( nullptr ), mnICOptions( InputContextFlags::NONE ), mnBlinkCursorDelay( nMinBlinkCursorDelay ), - mbForceFlush( false ) + mbForceFlush( false ), + mbInternalFullScreen( false ), + maInternalFullScreenRestoreRect( NSZeroRect ), + maInternalFullScreenExpectedRect( NSZeroRect ), + mbNativeFullScreen( false ), + maNativeFullScreenRestoreRect( NSZeroRect ) { mpParent = dynamic_cast<AquaSalFrame*>(pParent); @@ -124,7 +128,7 @@ AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle AquaSalFrame::~AquaSalFrame() { - if (mbFullScreen) + if (mbInternalFullScreen) doShowFullScreen(false, maGeometry.screen()); assert( GetSalData()->mpInstance->IsMainThread() ); @@ -404,7 +408,7 @@ void AquaSalFrame::initShow() OSX_SALDATA_RUNINMAIN( initShow() ) mbInitShow = false; - if( ! mbPositioned && ! mbFullScreen ) + if( ! mbPositioned && ! mbInternalFullScreen ) { AbsoluteScreenPixelRectangle aScreenRect; GetWorkArea( aScreenRect ); @@ -545,7 +549,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]; } } @@ -676,7 +684,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()); @@ -687,7 +695,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) @@ -746,19 +754,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; } @@ -814,22 +856,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]; @@ -840,51 +878,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(); @@ -1699,7 +1796,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; @@ -1714,7 +1811,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 @@ -1745,7 +1842,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(); @@ -1990,7 +2087,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 2570581cb140..ad868d396d59 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 @@ -282,6 +323,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 @@ -355,7 +397,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]; } @@ -381,12 +423,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 @@ -408,6 +452,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 @@ -590,24 +653,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 @@ -785,6 +882,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 0dbaa428a83a..3983d32241a3 100644 --- a/vcl/osx/salinst.cxx +++ b/vcl/osx/salinst.cxx @@ -469,7 +469,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 aeb3cfdd9f4e..e426d682acd2 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 |