mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
IDEA-256329 Big Sur: wrong z-order of dialogs and alerts causing UI lock
GitOrigin-RevId: 2653b08c52b195835171868924b36cbeb044ca71
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1330933f66
commit
0d2b9594cb
@@ -15,18 +15,28 @@
|
||||
*/
|
||||
package com.intellij.internal.validation;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.ui.newItemPopup.NewItemPopupUtil;
|
||||
import com.intellij.ide.ui.newItemPopup.NewItemSimplePopupPanel;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.popup.JBPopup;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.ui.components.panels.VerticalLayout;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
@@ -36,7 +46,9 @@ public class TestMacMessagesAction extends AnAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
new DialogWrapper(e.getProject()) {
|
||||
final Project project = e.getProject();
|
||||
|
||||
new DialogWrapper(project) {
|
||||
{
|
||||
setSize(500, 500);
|
||||
setTitle("Dialog 1");
|
||||
@@ -85,7 +97,7 @@ public class TestMacMessagesAction extends AnAction {
|
||||
|
||||
JButton ok = new JButton("Show Ok Alert");
|
||||
ok.addActionListener(event -> {
|
||||
Messages.showInfoMessage("Message", "Title");
|
||||
Messages.showInfoMessage(ok, "Message", "Title");
|
||||
});
|
||||
panel.add(ok);
|
||||
|
||||
@@ -101,7 +113,7 @@ public class TestMacMessagesAction extends AnAction {
|
||||
alertWithButtons(panel, "Show Buttons with Cancel Alert", new String[]{"Button1", "Button2", "Button3", "Button4", "Cancel"});
|
||||
|
||||
JButton dialogAlert = new JButton("Dialog -> YesNo Alert");
|
||||
dialogAlert.addActionListener(event -> new DialogWrapper(e.getProject()) {
|
||||
dialogAlert.addActionListener(event -> new DialogWrapper(project) {
|
||||
{
|
||||
setSize(400, 400);
|
||||
setTitle("Dialog 2");
|
||||
@@ -115,6 +127,7 @@ public class TestMacMessagesAction extends AnAction {
|
||||
b.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e1) {
|
||||
getWindow().setVisible(false);
|
||||
Messages.showYesNoDialog(b, "Message", "Title", Messages.getQuestionIcon());
|
||||
}
|
||||
});
|
||||
@@ -125,7 +138,7 @@ public class TestMacMessagesAction extends AnAction {
|
||||
|
||||
|
||||
JButton changeTitleDialog = new JButton("Dialog(dynamic title) -> Alert");
|
||||
changeTitleDialog.addActionListener(event -> new DialogWrapper(e.getProject()) {
|
||||
changeTitleDialog.addActionListener(event -> new DialogWrapper(project) {
|
||||
{
|
||||
setSize(400, 400);
|
||||
setTitle("Dialog [0]");
|
||||
@@ -170,7 +183,7 @@ public class TestMacMessagesAction extends AnAction {
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
DialogWrapper dialog = new DialogWrapper(e.getProject()) {
|
||||
DialogWrapper dialog = new DialogWrapper(project) {
|
||||
{
|
||||
setSize(400, 400);
|
||||
setTitle("Dialog");
|
||||
@@ -212,6 +225,65 @@ public class TestMacMessagesAction extends AnAction {
|
||||
});
|
||||
panel.add(html);
|
||||
|
||||
JButton decompiler = new JButton("Decompiler");
|
||||
decompiler.addActionListener(event -> {
|
||||
System.out.println(Messages.showDialog(decompiler,
|
||||
"IMPORTANT: BY ACCESSING AND USING JETBRAINS DECOMPILER, YOU AGREE TO THE CERTAIN TERMS AND CONDITIONS SET FORTH IN THE END-USER LICENSE AGREEMENT AND QUOTED BELOW. IF YOU DO NOT AGREE WITH THESE TERMS OR CONDITIONS, DO NOT ACCESS OR USE JETBRAINS DECOMPILER. The Software includes decompiling functionality (\"\"JetBrains Decompiler\"\") that enables reproducing source code from the original binary code. Licensee aknowledges that binary code and source code might be protected by copyright and trademark laws. Before using JetBrains Decompiler, Licensee should make sure that decompilation of binary code is not prohibited by the applicable license agreement (except to the extent that Licensee may be expressly permitted under applicable law) or that Licensee has obtained permission to decompile the binary code from the copyright owner. Using JetBrains Decompiler is entirely optional. Licensor does neither encourage nor condone the use of JetBrains Decompiler, and disclaims any liability for Licensee's use of JetBrains Decompiler in violation of applicable laws.",
|
||||
"\n\n\n\n\nDecompiler Legal Notice — Accept", new String[]{"Yes", "No"}, 0, null));
|
||||
});
|
||||
panel.add(decompiler);
|
||||
|
||||
JButton progress = new JButton("Porgress");
|
||||
progress.addActionListener(event -> {
|
||||
if (Messages.showYesNoDialog("Continue?", "Title", null) != Messages.YES) {
|
||||
return;
|
||||
}
|
||||
Runnable runnable = () -> {
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
Semaphore done = new Semaphore();
|
||||
done.down();
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
try {
|
||||
int count = new Random(System.currentTimeMillis()).nextInt(10);
|
||||
indicator.setText("Count: " + count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Thread.sleep(1000);
|
||||
indicator.setText("Count: " + i + "/" + count);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
done.up();
|
||||
}
|
||||
});
|
||||
|
||||
while (!done.waitFor(1000)) {
|
||||
if (indicator.isCanceled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> Messages.showInfoMessage("Finish11111", "Title"), ModalityState.any());
|
||||
};
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Progress", true, null, progress);
|
||||
Messages.showInfoMessage("Finish", "Title");
|
||||
});
|
||||
panel.add(progress);
|
||||
|
||||
JButton popupButton = new JButton("Popup");
|
||||
popupButton.addActionListener(event -> {
|
||||
NewItemSimplePopupPanel contentPanel = new NewItemSimplePopupPanel();
|
||||
JTextField nameField = contentPanel.getTextField();
|
||||
JBPopup popup = NewItemPopupUtil.createNewItemPopup(IdeBundle.message("title.new.file"), contentPanel, nameField);
|
||||
contentPanel.setApplyAction(_event -> {
|
||||
Messages.showYesNoCancelDialog("Message", "Title", "YesText", "NoText", "CancelText", null);
|
||||
});
|
||||
popup.showCenteredInCurrentWindow(project);
|
||||
});
|
||||
panel.add(popupButton);
|
||||
|
||||
return panel;
|
||||
}
|
||||
}.show();
|
||||
|
||||
@@ -22,8 +22,12 @@ import com.intellij.ui.mac.foundation.MacUtil
|
||||
import com.intellij.util.ReflectionUtil
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.sun.jna.Callback
|
||||
import java.awt.*
|
||||
import java.awt.event.WindowEvent
|
||||
import java.awt.Component
|
||||
import java.awt.Dialog
|
||||
import java.awt.Toolkit
|
||||
import java.awt.Window
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.lang.reflect.Proxy
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.SwingUtilities
|
||||
@@ -62,9 +66,9 @@ private class MessageInfo(val title: String,
|
||||
|
||||
val window: Window?
|
||||
val nativeWindow: ID?
|
||||
val menuWindow: ID
|
||||
var alertWindow: ID? = null
|
||||
|
||||
var mainHandler = {}
|
||||
var disposer = {}
|
||||
|
||||
lateinit var dialog: MyDialog
|
||||
@@ -78,7 +82,8 @@ private class MessageInfo(val title: String,
|
||||
popupWindow = SwingUtilities.getWindowAncestor(popup.content)
|
||||
}
|
||||
if (popupWindow == null) {
|
||||
val visibleWindow = getVisibleWindow(window ?: JBMacMessages.getForemostWindow())
|
||||
val parentWindow = window ?: JBMacMessages.getForemostWindow()
|
||||
val visibleWindow = getVisibleWindow(parentWindow)
|
||||
if (visibleWindow != null && visibleWindow.parent == null && visibleWindow.ownedWindows.all { !it.isVisible }) {
|
||||
this.window = visibleWindow
|
||||
this.nativeWindow = MacUtil.getWindowFromJavaWindow(visibleWindow)
|
||||
@@ -87,10 +92,12 @@ private class MessageInfo(val title: String,
|
||||
this.window = null
|
||||
this.nativeWindow = null
|
||||
}
|
||||
this.menuWindow = MacUtil.getWindowFromJavaWindow(getOwner(parentWindow))
|
||||
}
|
||||
else {
|
||||
this.window = null
|
||||
this.nativeWindow = null
|
||||
this.menuWindow = MacUtil.getWindowFromJavaWindow(getOwner(popupWindow))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,15 +223,6 @@ private class NativeMacMessageManager : MacMessages() {
|
||||
LOG.info("=== MacAlert: show alert during show another alert ===", Throwable())
|
||||
}
|
||||
|
||||
info.mainHandler = {
|
||||
Foundation.invoke(Foundation.invoke(info.nativeWindow, "delegate"), "activateWindowMenuBar")
|
||||
info.dialog.orderAboveSiblings()
|
||||
|
||||
info.mainHandler = {
|
||||
info.dialog.orderAboveSiblings()
|
||||
}
|
||||
}
|
||||
|
||||
info.dialog = MyDialog(info.window) {
|
||||
val delegate = Foundation.invoke(Foundation.invoke(Foundation.getObjcClass("NSJavaAlertDelegate"), "alloc"), "init")
|
||||
Foundation.invoke(delegate, "performSelectorOnMainThread:withObject:waitUntilDone:", Foundation.createSelector("showAlert:"),
|
||||
@@ -232,11 +230,20 @@ private class NativeMacMessageManager : MacMessages() {
|
||||
info.disposer = {
|
||||
Foundation.cfRelease(delegate)
|
||||
Foundation.executeOnMainThread(false, false) {
|
||||
Foundation.invoke(Foundation.invoke(info.nativeWindow, "delegate"), "activateWindowMenuBar")
|
||||
Foundation.invoke(Foundation.invoke(info.menuWindow, "delegate"), "activateWindowMenuBar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.dialog.addComponentListener(object : ComponentAdapter() {
|
||||
override fun componentShown(e: ComponentEvent) {
|
||||
info.dialog.removeComponentListener(this)
|
||||
Foundation.executeOnMainThread(false, false) {
|
||||
Foundation.invoke(Foundation.invoke(info.menuWindow, "delegate"), "activateWindowMenuBar")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
IdeFocusManager.getGlobalInstance().setTypeaheadEnabled(false)
|
||||
StackingPopupDispatcher.getInstance().hidePersistentPopups()
|
||||
@@ -346,7 +353,7 @@ private class NativeMacMessageManager : MacMessages() {
|
||||
}
|
||||
|
||||
if (ownerWindow == null) {
|
||||
setResult(alert, Foundation.invoke(alert, "runModal"), index)
|
||||
setResult(alert, Foundation.invoke(alert, "runModal"), index, false)
|
||||
}
|
||||
else {
|
||||
Foundation.invoke(alert, "beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:", ownerWindow, self,
|
||||
@@ -358,15 +365,15 @@ private class NativeMacMessageManager : MacMessages() {
|
||||
private val ALERT_DID_END = object : Callback {
|
||||
@Suppress("UNUSED_PARAMETER", "unused")
|
||||
fun callback(self: ID, selector: String, alert: ID, returnCode: ID, contextInfo: ID) {
|
||||
setResult(alert, returnCode, Foundation.invoke(contextInfo, "intValue").toInt())
|
||||
setResult(alert, returnCode, Foundation.invoke(contextInfo, "intValue").toInt(), true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setResult(alert: ID, returnCode: ID, index: Int) {
|
||||
private fun setResult(alert: ID, returnCode: ID, index: Int, resetHandler: Boolean) {
|
||||
val info = getInfo(index)
|
||||
info.result = returnCode.toInt()
|
||||
info.suppress = Foundation.invoke(Foundation.invoke(alert, "suppressionButton"), "state").toInt() == 1
|
||||
info.dialog.close()
|
||||
info.dialog.close(resetHandler)
|
||||
Foundation.cfRelease(alert)
|
||||
}
|
||||
|
||||
@@ -378,7 +385,7 @@ private class NativeMacMessageManager : MacMessages() {
|
||||
it != null && self == it.alertWindow
|
||||
}
|
||||
}
|
||||
info?.mainHandler?.invoke()
|
||||
info?.dialog?.orderAboveSiblings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +431,10 @@ private fun enableEscapeToCloseTheMessage(alert: ID) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOwner(window: Window): Window {
|
||||
return window.owner?.let { getOwner(it) } ?: window
|
||||
}
|
||||
|
||||
private class MyDialog(parent: Window?, private val runnable: () -> Unit) : JDialog(parent, ModalityType.APPLICATION_MODAL) {
|
||||
private var myPlatformWindow: Any? = null
|
||||
|
||||
@@ -477,24 +488,23 @@ private class MyDialog(parent: Window?, private val runnable: () -> Unit) : JDia
|
||||
}
|
||||
super.hide()
|
||||
if (blockedWindows != null) {
|
||||
val owner = getOwner(this)
|
||||
for (window in blockedWindows) {
|
||||
if (owner === getOwner(window)) {
|
||||
if (window.isVisible) {
|
||||
window.toFront()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOwner(window: Window): Window {
|
||||
return window.owner?.let { getOwner(it) } ?: window
|
||||
fun setHandler(value: Long) {
|
||||
UIUtil.invokeLaterIfNeeded(Runnable { setHandlerValue(value) })
|
||||
}
|
||||
|
||||
fun setHandler(handler: Long) {
|
||||
fun setHandlerValue(value: Long) {
|
||||
try {
|
||||
ReflectionUtil.getDeclaredMethod(Class.forName("sun.lwawt.macosx.CFRetainedResource"), "setPtr",
|
||||
Long::class.javaPrimitiveType)!!.invoke(myPlatformWindow, handler)
|
||||
ReflectionUtil.getDeclaredField(myPlatformWindow!!.javaClass.getSuperclass(), "visible")!!.setBoolean(myPlatformWindow, handler != 0L)
|
||||
Long::class.javaPrimitiveType)!!.invoke(myPlatformWindow, value)
|
||||
ReflectionUtil.getDeclaredField(myPlatformWindow!!.javaClass.getSuperclass(), "visible")!!.setBoolean(myPlatformWindow, value != 0L)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.error(e)
|
||||
@@ -510,15 +520,13 @@ private class MyDialog(parent: Window?, private val runnable: () -> Unit) : JDia
|
||||
}
|
||||
}
|
||||
|
||||
fun close() {
|
||||
setHandler(0)
|
||||
|
||||
try {
|
||||
ReflectionUtil.getDeclaredMethod(Class.forName("sun.lwawt.LWToolkit"), "postEvent",
|
||||
AWTEvent::class.java)!!.invoke(null, WindowEvent(this, WindowEvent.WINDOW_CLOSING))
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.error(e)
|
||||
}
|
||||
fun close(resetHandler: Boolean) {
|
||||
UIUtil.invokeLaterIfNeeded(Runnable {
|
||||
if (resetHandler) {
|
||||
setHandlerValue(0)
|
||||
}
|
||||
@Suppress("SSBasedInspection")
|
||||
dispose()
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user