fix non-nested modality state support

This commit is contained in:
peter
2016-04-27 10:39:39 +02:00
parent 3d16c0a848
commit 409a54d1d1
3 changed files with 67 additions and 107 deletions
@@ -17,44 +17,30 @@ package com.intellij.openapi.application.impl;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.WeakList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ModalityStateEx extends ModalityState {
private static final WeakReference[] EMPTY_REFS_ARRAY = new WeakReference[0];
private final WeakList myModalEntities = new WeakList();
private final WeakReference[] myModalEntities;
public ModalityStateEx() {
this(EMPTY_REFS_ARRAY);
}
public ModalityStateEx() { } // used by reflection to initialize NON_MODAL
public ModalityStateEx(@NotNull Object[] modalEntities) {
if (modalEntities.length > 0) {
myModalEntities = new WeakReference[modalEntities.length];
for (int i = 0; i < modalEntities.length; i++) {
Object entity = modalEntities[i];
myModalEntities[i] = new WeakReference<Object>(entity);
}
}
else{
myModalEntities = EMPTY_REFS_ARRAY;
}
Collections.addAll(myModalEntities, modalEntities);
}
private List<Object> getModalEntities() {
return ContainerUtil.mapNotNull(myModalEntities, new Function<WeakReference, Object>() {
@Override
public Object fun(WeakReference reference) {
return reference.get();
}
});
ArrayList<Object> result = new ArrayList<Object>();
for (Object entity : myModalEntities) {
result.add(entity);
}
return result;
}
@NotNull
@@ -64,8 +50,9 @@ public class ModalityStateEx extends ModalityState {
@NotNull
ModalityStateEx appendEntity(@NotNull Object anEntity){
List<Object> list = new ArrayList<Object>(myModalEntities.length+1);
list.addAll(getModalEntities());
List<Object> modalEntities = getModalEntities();
List<Object> list = new ArrayList<Object>(modalEntities.size() + 1);
list.addAll(modalEntities);
list.add(anEntity);
return new ModalityStateEx(list.toArray());
}
@@ -81,21 +68,10 @@ public class ModalityStateEx extends ModalityState {
return false;
}
boolean contains(@NotNull Object modalEntity) {
return getModalEntities().contains(modalEntity);
}
@NonNls
public String toString() {
if (myModalEntities.length == 0) return "ModalityState.NON_MODAL";
@NonNls StringBuilder buffer = new StringBuilder();
buffer.append("ModalityState:");
for (int i = 0; i < myModalEntities.length; i++) {
Object entity = myModalEntities[i].get();
if (i > 0) buffer.append(", ");
buffer.append(entity);
}
return buffer.toString();
List<Object> modalEntities = getModalEntities();
return modalEntities.isEmpty() ? "ModalityState.NON_MODAL" : "ModalityState:" + StringUtil.join(modalEntities, ", ");
}
@Override
@@ -113,4 +89,8 @@ public class ModalityStateEx extends ModalityState {
public int hashCode() {
return getModalEntities().hashCode();
}
void removeModality(Object modalEntity) {
myModalEntities.remove(modalEntity);
}
}
@@ -41,7 +41,6 @@ import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -89,8 +88,6 @@ public class LaterInvocator {
private static final EventDispatcher<ModalityStateListener> ourModalityStateMulticaster = EventDispatcher.create(ModalityStateListener.class);
private static final List<RunnableInfo> ourForcedFlushQueue = new ArrayList<RunnableInfo>();
public static void addModalityStateListener(@NotNull ModalityStateListener listener, @NotNull Disposable parentDisposable) {
if (!ourModalityStateMulticaster.getListeners().contains(listener)) {
ourModalityStateMulticaster.addListener(listener, parentDisposable);
@@ -204,43 +201,25 @@ public class LaterInvocator {
LOG.debug("leaveModal:" + modalEntity);
}
//noinspection StatementWithEmptyBody
while (ourFlushQueueRunnable.runNextEvent());
ourModalityStateMulticaster.getMulticaster().beforeModalityStateChanged(false);
boolean removed = ourModalEntities.remove(modalEntity);
if (ourModalityStack.size() > 1) {
ourModalityStack.pop();
int index = ourModalEntities.indexOf(modalEntity);
LOG.assertTrue(index >= 0);
ourModalEntities.remove(index);
ourModalityStack.remove(index + 1);
for (int i = 1; i < ourModalityStack.size(); i++) {
((ModalityStateEx)ourModalityStack.get(i)).removeModality(modalEntity);
}
LOG.assertTrue(removed, modalEntity);
LOG.assertTrue(!ourModalityStack.isEmpty());
TransactionGuardImpl guard = IdeaApplication.isLoaded() ? (TransactionGuardImpl)TransactionGuard.getInstance() : null;
if (guard != null) {
guard.leftModality(modalEntity);
}
cleanupQueueForModal(modalEntity);
ourQueueSkipCount = 0;
requestFlush();
}
private static void cleanupQueueForModal(@NotNull final Object modalEntity) {
synchronized (LOCK) {
for (Iterator<RunnableInfo> iterator = ourQueue.iterator(); iterator.hasNext(); ) {
RunnableInfo runnableInfo = iterator.next();
if (runnableInfo.modalityState instanceof ModalityStateEx) {
ModalityStateEx stateEx = (ModalityStateEx)runnableInfo.modalityState;
if (stateEx.contains(modalEntity)) {
ourForcedFlushQueue.add(runnableInfo);
iterator.remove();
}
}
}
}
}
@TestOnly
public static void leaveAllModals() {
while (!ourModalEntities.isEmpty()) {
@@ -298,17 +277,6 @@ public class LaterInvocator {
@Nullable
private static RunnableInfo getNextEvent(boolean remove) {
synchronized (LOCK) {
if (!ourForcedFlushQueue.isEmpty()) {
final RunnableInfo toRun = remove ? ourForcedFlushQueue.remove(0) : ourForcedFlushQueue.get(0);
if (!toRun.expired.value(null)) {
return toRun;
}
else {
toRun.callback.setDone();
}
}
ModalityState currentModality = getCurrentModalityState();
while (ourQueueSkipCount < ourQueue.size()) {
@@ -202,35 +202,6 @@ public class LaterInvocatorTest extends PlatformTestCase {
});
}
public void testRunQueuedRunnablesOnLeavingModality() {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL);
LaterInvocator.invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL);
flushSwingQueue();
checkOrder(0);
final ModalityState modalityState = ModalityState.stateForComponent(myWindow1);
LaterInvocator.invokeLater(new MyRunnable("1") {
@Override
public void run() {
super.run();
checkOrder(1);
LaterInvocator.invokeLater(new MyRunnable("2"), modalityState);
checkOrder(1);
LaterInvocator.leaveModal(myWindow1);
checkOrder(2);
}
}, modalityState);
flushSwingQueue(); // let "1" run
flushSwingQueue(); // let "3" run
checkOrder(3);
}
});
}
public void testStress() throws Exception {
UIUtil.invokeAndWaitIfNeeded((Runnable)() -> {
int N = 1000;
@@ -551,4 +522,45 @@ public class LaterInvocatorTest extends PlatformTestCase {
assertSame(modalityState1, ModalityState.current());
});
}
public void testNonNestedModalityState() { //happens with per-project modality
Object modal1 = new Object();
Object modal2 = new Object();
UIUtil.invokeAndWaitIfNeeded((Runnable)() -> {
LaterInvocator.enterModal(modal1); // [modal1]
ModalityState ms_1 = ModalityState.current();
ApplicationManager.getApplication().invokeLater(new MyRunnable("m1"), ms_1);
LaterInvocator.enterModal(modal2); //[modal1, modal2]
ModalityState ms_12 = ModalityState.current();
assertNotSame(ms_1, ms_12);
assertTrue(ms_12.dominates(ms_1));
UIUtil.dispatchAllInvocationEvents();
assertEmpty(myOrder);
ApplicationManager.getApplication().invokeLater(new MyRunnable("m12"), ms_12);
LaterInvocator.leaveModal(modal1); // [modal2]
assertEmpty(myOrder);
UIUtil.dispatchAllInvocationEvents();
assertOrderedEquals(myOrder, "m12");
ModalityState ms_2 = ModalityState.current();
assertSame(ms_12, ms_2);
assertTrue(ms_2.dominates(ms_1));
ApplicationManager.getApplication().invokeLater(new MyRunnable("m1x"), ms_1);
ApplicationManager.getApplication().invokeLater(new MyRunnable("m2"), ms_2);
UIUtil.dispatchAllInvocationEvents();
assertOrderedEquals(myOrder, "m12", "m2");
LaterInvocator.leaveModal(modal2); // NON_MODAL
UIUtil.dispatchAllInvocationEvents();
assertOrderedEquals(myOrder, "m12", "m2", "m1", "m1x");
});
}
}