mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
write action progress: paint only in EDT, don't reschedule events after finishing
instead, consume input events when they're posted
This commit is contained in:
@@ -122,6 +122,8 @@ public class IdeEventQueue extends EventQueue {
|
||||
private boolean myDispatchingFocusEvent;
|
||||
private boolean myWinMetaPressed;
|
||||
private int myInputMethodLock;
|
||||
private final com.intellij.util.EventDispatcher<PostEventHook>
|
||||
myPostEventListeners = com.intellij.util.EventDispatcher.create(PostEventHook.class);
|
||||
|
||||
private static class IdeEventQueueHolder {
|
||||
private static final IdeEventQueue INSTANCE = new IdeEventQueue();
|
||||
@@ -1121,6 +1123,10 @@ public class IdeEventQueue extends EventQueue {
|
||||
private final FrequentEventDetector myFrequentEventDetector = new FrequentEventDetector(1009, 100);
|
||||
@Override
|
||||
public void postEvent(@NotNull AWTEvent event) {
|
||||
for (PostEventHook listener : myPostEventListeners.getListeners()) {
|
||||
if (listener.consumePostedEvent(event)) return;
|
||||
}
|
||||
|
||||
myFrequentEventDetector.eventHappened(event);
|
||||
if (isKeyboardEvent(event)) {
|
||||
myKeyboardEventsPosted.incrementAndGet();
|
||||
@@ -1163,4 +1169,18 @@ public class IdeEventQueue extends EventQueue {
|
||||
public enum BlockMode {
|
||||
COMPLETE, ACTIONS
|
||||
}
|
||||
|
||||
/**
|
||||
* An absolutely guru API, please avoid using it at all cost.
|
||||
*/
|
||||
public interface PostEventHook extends EventListener {
|
||||
/**
|
||||
* @return true if event is handled by the listener and should't be added to event queue at all
|
||||
*/
|
||||
boolean consumePostedEvent(@NotNull AWTEvent event);
|
||||
}
|
||||
|
||||
public void addPostEventListener(@NotNull PostEventHook listener, @NotNull Disposable parentDisposable) {
|
||||
myPostEventListeners.addListener(listener, parentDisposable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ public class TestWriteActionUnderProgress extends DumbAwareAction {
|
||||
private static void runIndeterminateProgress(ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(true);
|
||||
indicator.setText("Indeterminate");
|
||||
for (int i = 0; i < 200; i++) {
|
||||
TimeoutUtil.sleep(10);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
TimeoutUtil.sleep(5);
|
||||
indicator.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
+42
-41
@@ -15,22 +15,24 @@
|
||||
*/
|
||||
package com.intellij.openapi.progress.util;
|
||||
|
||||
import com.intellij.concurrency.JobScheduler;
|
||||
import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.awt.SunToolkit;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A progress indicator for processes running in EDT. Paints itself in checkCanceled calls.
|
||||
@@ -38,15 +40,32 @@ import java.util.Objects;
|
||||
* @author peter
|
||||
*/
|
||||
public class PotemkinProgress extends ProgressWindow {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.util.PotemkinProgress");
|
||||
private long myLastUiUpdate = System.currentTimeMillis();
|
||||
private final List<AWTEvent> myDelayedEvents = new ArrayList<>();
|
||||
private IdeEventQueue myEventQueue = IdeEventQueue.getInstance();
|
||||
private final Queue<InputEvent> myEventQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public PotemkinProgress(@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent, @Nullable String cancelText) {
|
||||
super(cancelText != null,false, project, parentComponent, cancelText);
|
||||
setTitle(title);
|
||||
installCheckCanceledPaintingHook();
|
||||
startStealingInputEvents();
|
||||
}
|
||||
|
||||
private void startStealingInputEvents() {
|
||||
checkNativeEventsRegularly();
|
||||
|
||||
IdeEventQueue.getInstance().addPostEventListener(event -> {
|
||||
if (event instanceof InputEvent) {
|
||||
myEventQueue.offer((InputEvent)event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, this);
|
||||
}
|
||||
|
||||
private void checkNativeEventsRegularly() {
|
||||
ScheduledFuture<?> future = JobScheduler.getScheduler().scheduleWithFixedDelay(
|
||||
() -> SunToolkit.flushPendingEvents(), 3, 3, TimeUnit.MILLISECONDS);
|
||||
Disposer.register(this, () -> future.cancel(false));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -63,48 +82,42 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
addStateDelegate(new AbstractProgressIndicatorExBase() {
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
dispatchAwtEventsWithoutModelAccess();
|
||||
updateUI();
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
dispatchAwtEventsWithoutModelAccess();
|
||||
updateUI();
|
||||
}
|
||||
return super.isCanceled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchAwtEventsWithoutModelAccess() {
|
||||
while (myEventQueue.peekEvent() != null) {
|
||||
try {
|
||||
handleEvent(myEventQueue.getNextEvent());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOG.error(e);
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
InputEvent event = myEventQueue.poll();
|
||||
if (event == null) return;
|
||||
|
||||
dispatchInputEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEvent(AWTEvent e) {
|
||||
if (e instanceof InputEvent) {
|
||||
dispatchInputEvent(e);
|
||||
} else {
|
||||
myDelayedEvents.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchInputEvent(AWTEvent e) {
|
||||
private void dispatchInputEvent(InputEvent e) {
|
||||
if (isCancellationEvent(e)) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
Object source = e.getSource();
|
||||
if (source instanceof Component && getDialog().getPanel().isAncestorOf((Component)source)) {
|
||||
if (source instanceof Component && isInDialogWindow((Component)source)) {
|
||||
((Component)source).dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (!ApplicationManager.getApplication().isDispatchThread()) return;
|
||||
private boolean isInDialogWindow(Component source) {
|
||||
Window dialogWindow = SwingUtilities.windowForComponent(getDialog().getPanel());
|
||||
return dialogWindow instanceof JDialog && SwingUtilities.isDescendingFrom(source, dialogWindow);
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
JRootPane rootPane = getDialog().getPanel().getRootPane();
|
||||
if (rootPane == null) {
|
||||
rootPane = considerShowingDialog();
|
||||
@@ -136,18 +149,6 @@ public class PotemkinProgress extends ProgressWindow {
|
||||
|
||||
public void progressFinished() {
|
||||
getDialog().hideImmediately();
|
||||
scheduleDelayedEventDelivery();
|
||||
}
|
||||
|
||||
private void scheduleDelayedEventDelivery() {
|
||||
Disposable disposable = Disposer.newDisposable();
|
||||
myEventQueue.addDispatcher(e -> {
|
||||
Disposer.dispose(disposable);
|
||||
for (AWTEvent event : myDelayedEvents) {
|
||||
myEventQueue.dispatchEvent(event);
|
||||
}
|
||||
return false;
|
||||
}, disposable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user