diff --git a/platform/platform-impl/src/com/intellij/internal/validation/TestMacMessagesAction.java b/platform/platform-impl/src/com/intellij/internal/validation/TestMacMessagesAction.java index ca85115a4706..0cd7e987edea 100644 --- a/platform/platform-impl/src/com/intellij/internal/validation/TestMacMessagesAction.java +++ b/platform/platform-impl/src/com/intellij/internal/validation/TestMacMessagesAction.java @@ -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(); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/MacMessageManagerImpl.kt b/platform/platform-impl/src/com/intellij/ui/messages/MacMessageManagerImpl.kt index 5d01b531e5d7..20ab7c3362af 100644 --- a/platform/platform-impl/src/com/intellij/ui/messages/MacMessageManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/ui/messages/MacMessageManagerImpl.kt @@ -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() + }) } } \ No newline at end of file