mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[settings] IJPL-245458 IJPL-245379 Non-modal settings: fix window disappearing when pinned from macOS full-screen
When the Settings window (unpinned/window mode) was in macOS native full-screen and the user pressed Pin, the new FloatDialog was created while macOS was still running the Space-exit animation, making it invisible until a Cmd+Tab round-trip. Fix: switchWindowMode now detects native full-screen via MacFullScreenSupport and defers doSwitchWindowMode until windowExitedFullScreen fires. MacFullScreenSupport gains exitFullScreen(window, onExited) that triggers requestToggleFullScreen and routes the completion callback through the primary FullScreenListener (the only one that JBR reliably delivers events to) (cherry picked from commit dbb0b15c6817d66a07560897457038d02378addc) IJ-CR-217470 GitOrigin-RevId: f5112ce355632d272129f107a68061f08acf6520
This commit is contained in:
committed by
intellij-monorepo-bot
parent
afe2923d9c
commit
6c2d42e871
@@ -33,6 +33,7 @@ import com.intellij.ui.ComponentUtil
|
||||
import com.intellij.ui.FullScreenSupport
|
||||
import com.intellij.ui.ScreenUtil
|
||||
import com.intellij.ui.ToolbarService
|
||||
import com.intellij.ui.mac.MacFullScreenSupport
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.launchOnShow
|
||||
@@ -119,6 +120,8 @@ abstract class NonModalWindowWrapper(
|
||||
private lateinit var minWindowSize: Dimension
|
||||
private var windowListener: WindowAdapter? = null
|
||||
private var windowDisposable: Disposable? = null
|
||||
/** Non-null only on macOS while [activeWindow] is a [WindowFrame]. Tracks native full-screen state. */
|
||||
private var fullScreenSupport: FullScreenSupport? = null
|
||||
|
||||
protected var isFloat: Boolean
|
||||
get() = PropertiesComponent.getInstance().getBoolean(floatModeKey, true)
|
||||
@@ -303,10 +306,12 @@ abstract class NonModalWindowWrapper(
|
||||
frame.accessibleContext.accessibleName = getAccessibleWindowName()
|
||||
val wd = Disposer.newDisposable(frameDisposable)
|
||||
windowDisposable = wd
|
||||
val fss = FullScreenSupport.NEW.apply("com.intellij.ui.mac.MacFullScreenSupport")
|
||||
fullScreenSupport = fss
|
||||
ToolbarService.getInstance().setTransparentTitleBar(
|
||||
window = frame,
|
||||
rootPane = frame.rootPane,
|
||||
handlerProvider = { FullScreenSupport.NEW.apply("com.intellij.ui.mac.MacFullScreenSupport") },
|
||||
handlerProvider = { fss },
|
||||
onDispose = { runnable -> Disposer.register(wd) { runnable.run() } },
|
||||
)
|
||||
}
|
||||
@@ -381,24 +386,47 @@ abstract class NonModalWindowWrapper(
|
||||
/**
|
||||
* Switches between Float ([JDialog]) and Window ([JFrame]) mode.
|
||||
* The [content] component is reparented and window bounds are transferred.
|
||||
*
|
||||
* On macOS, if the [WindowFrame] is in native full-screen when the user pins the window
|
||||
* (toFloat=true, i.e. switching FROM WindowFrame TO FloatDialog), we first exit full-screen
|
||||
* via [MacFullScreenSupport.exitFullScreen] and defer the actual switch until the OS animation
|
||||
* completes. This ensures the new [FloatDialog] is created after macOS has restored the
|
||||
* pre-full-screen window bounds. On non-macOS, [fullScreenSupport] is always null.
|
||||
*/
|
||||
private fun switchWindowMode(toFloat: Boolean) {
|
||||
val bounds = activeWindow.bounds
|
||||
val macFss = if (toFloat) fullScreenSupport as? MacFullScreenSupport else null
|
||||
if (macFss != null && macFss.isFullScreen) {
|
||||
val ideFrame = getIdeJFrame()
|
||||
macFss.exitFullScreen(activeWindow) {
|
||||
if (!isDisposed) doSwitchWindowMode(toFloat, ideFrame)
|
||||
}
|
||||
return
|
||||
}
|
||||
doSwitchWindowMode(toFloat)
|
||||
}
|
||||
|
||||
private fun doSwitchWindowMode(toFloat: Boolean, ideFrameToActivate: JFrame? = null) {
|
||||
val oldBounds = activeWindow.bounds
|
||||
val wasVisible = activeWindow.isVisible
|
||||
val savedDefaultButton = (activeWindow as RootPaneContainer).rootPane.defaultButton
|
||||
windowListener?.let { activeWindow.removeWindowListener(it) }
|
||||
windowListener = null
|
||||
content.parent?.remove(content)
|
||||
fullScreenSupport = null
|
||||
disposeWindow(activeWindow)
|
||||
windowDisposable?.let { Disposer.dispose(it) }
|
||||
windowDisposable = null
|
||||
activeWindow = createAwtWindow(toFloat, content, minWindowSize, bounds.size)
|
||||
|
||||
activeWindow = createAwtWindow(toFloat, content, minWindowSize, oldBounds.size)
|
||||
fitWindowToScreen(activeWindow)
|
||||
installWindowListeners()
|
||||
savedDefaultButton?.let { (activeWindow as RootPaneContainer).rootPane.defaultButton = it }
|
||||
activeWindow.bounds = bounds
|
||||
activeWindow.bounds = oldBounds
|
||||
dimensionKey?.let { WindowStateService.getInstance(project).getState(it, activeWindow) }
|
||||
if (wasVisible) {
|
||||
// When switching back from full-screen, first bring the IDE frame to front so macOS
|
||||
// navigates to the IDE Space, then show the dialog on top of it.
|
||||
ideFrameToActivate?.toFront()
|
||||
activeWindow.isVisible = true
|
||||
activeWindow.toFront()
|
||||
activeWindow.requestFocus()
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ui.mac;
|
||||
|
||||
import com.apple.eawt.Application;
|
||||
import com.apple.eawt.FullScreenListener;
|
||||
import com.apple.eawt.FullScreenUtilities;
|
||||
import com.apple.eawt.event.FullScreenEvent;
|
||||
import com.intellij.ui.FullScreenSupport;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.RootPaneContainer;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.Window;
|
||||
|
||||
/**
|
||||
@@ -22,6 +25,7 @@ import java.awt.Window;
|
||||
public final class MacFullScreenSupport implements FullScreenSupport {
|
||||
private FullScreenListener myListener;
|
||||
private boolean myIsFullScreen;
|
||||
private @Nullable Runnable myOnExitedCallback;
|
||||
|
||||
@Override
|
||||
public boolean isFullScreen() {
|
||||
@@ -49,11 +53,31 @@ public final class MacFullScreenSupport implements FullScreenSupport {
|
||||
@Override
|
||||
public void windowExitedFullScreen(FullScreenEvent event) {
|
||||
myIsFullScreen = false;
|
||||
Runnable callback = myOnExitedCallback;
|
||||
myOnExitedCallback = null;
|
||||
if (callback != null) {
|
||||
SwingUtilities.invokeLater(callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
FullScreenUtilities.addFullScreenListenerTo(window, myListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically exits macOS native full-screen and invokes {@code onExited} once the OS
|
||||
* animation completes (after {@code windowExitedFullScreen}). If the window is not in
|
||||
* full-screen, {@code onExited} is called immediately on the calling thread.
|
||||
* Not part of {@link FullScreenSupport} — this is a macOS-specific detail.
|
||||
*/
|
||||
public void exitFullScreen(@NotNull Window window, @NotNull Runnable onExited) {
|
||||
if (!myIsFullScreen) {
|
||||
onExited.run();
|
||||
return;
|
||||
}
|
||||
myOnExitedCallback = onExited;
|
||||
Application.getApplication().requestToggleFullScreen(window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(@NotNull Window window) {
|
||||
if (window instanceof RootPaneContainer container && container.getRootPane() != null) {
|
||||
|
||||
Reference in New Issue
Block a user