mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
runWriteActionWithProgress
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.internal;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.impl.ApplicationImpl;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.concurrency.EdtExecutorService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class TestWriteActionUnderProgress extends DumbAwareAction {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
ApplicationImpl app = (ApplicationImpl)ApplicationManager.getApplication();
|
||||
app.runWriteActionWithProgress("Progress in main frame", null, null, TestWriteActionUnderProgress::runIndeterminateProgress);
|
||||
|
||||
showDialog(app);
|
||||
}
|
||||
|
||||
private static void showDialog(ApplicationImpl app) {
|
||||
DialogWrapper dialog = new DialogWrapper(null) {
|
||||
{
|
||||
setTitle("Some Modal Dialog");
|
||||
init();
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return new JTextField("Waiting for the progress...");
|
||||
}
|
||||
};
|
||||
EdtExecutorService.getScheduledExecutorInstance().schedule(() -> {
|
||||
app.runWriteActionWithProgress("Progress in modal dialog", null, dialog.getRootPane(), TestWriteActionUnderProgress::runDeterminateProgress);
|
||||
dialog.close(0);
|
||||
}, 2500, TimeUnit.MILLISECONDS);
|
||||
app.invokeLater(() -> {
|
||||
dialog.setSize(100, 100);
|
||||
dialog.setLocation(100, 100);
|
||||
}, ModalityState.any());
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private static void runDeterminateProgress(ProgressIndicator indicator) {
|
||||
int iterations = 3000;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TimeoutUtil.sleep(1);
|
||||
indicator.setFraction(((double)i + 1) / ((double)iterations));
|
||||
indicator.setText(String.valueOf(i));
|
||||
ProgressManager.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
private static void runIndeterminateProgress(ProgressIndicator indicator) {
|
||||
indicator.setIndeterminate(true);
|
||||
indicator.setText("Indeterminate");
|
||||
for (int i = 0; i < 200; i++) {
|
||||
TimeoutUtil.sleep(10);
|
||||
indicator.checkCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.progress.*;
|
||||
import com.intellij.openapi.progress.impl.CoreProgressManager;
|
||||
import com.intellij.openapi.progress.util.ProgressWindow;
|
||||
import com.intellij.openapi.progress.util.PotemkinProgress;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
@@ -941,6 +942,19 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
myLock.readUnlock();
|
||||
}
|
||||
|
||||
public void runWriteActionWithProgress(@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent,
|
||||
@NotNull Consumer<ProgressIndicator> action) {
|
||||
Class<?> clazz = action.getClass();
|
||||
startWrite(clazz);
|
||||
try {
|
||||
PotemkinProgress indicator = new PotemkinProgress(title, project, parentComponent);
|
||||
ProgressManager.getInstance().runProcess(() -> action.consume(indicator), indicator);
|
||||
}
|
||||
finally {
|
||||
endWrite(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runWriteAction(@NotNull final Runnable action) {
|
||||
Class<? extends Runnable> clazz = action.getClass();
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.progress.util;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapperDialog;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.openapi.wm.IdeGlassPaneUtil;
|
||||
import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* A progress indicator for processes running in EDT. Paints itself in checkCanceled calls.
|
||||
*
|
||||
* @author peter
|
||||
*/
|
||||
public class PotemkinProgress extends ProgressWindow {
|
||||
private final BufferedImage myScreenshot;
|
||||
private long myLastUiUpdate = System.currentTimeMillis();
|
||||
|
||||
public PotemkinProgress(@NotNull String title, @Nullable Project project, @Nullable JComponent parentComponent) {
|
||||
super(false,false, project, parentComponent, null);
|
||||
setTitle(title);
|
||||
|
||||
ProgressDialog dialog = getDialog();
|
||||
Window parentWindow = dialog == null ? null : dialog.getParentWindow();
|
||||
myScreenshot = parentWindow instanceof IdeFrame ? takeScreenshot(((IdeFrame)parentWindow).getComponent()) : null;
|
||||
|
||||
installCheckCanceledPaintingHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember how everything looked. We must not call custom paint methods during write action,
|
||||
* because they might access the model which might be inconsistent at that moment.
|
||||
*/
|
||||
@NotNull
|
||||
private static BufferedImage takeScreenshot(@NotNull JComponent component) {
|
||||
BufferedImage image = UIUtil.createImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
component.paint(graphics);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private void installCheckCanceledPaintingHook() {
|
||||
// make ProgressManager#checkCanceled actually delegate to the current indicator
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
|
||||
// isCanceled is final, so using a nonstandard way of plugging into it
|
||||
addStateDelegate(new AbstractProgressIndicatorExBase() {
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
updateUI();
|
||||
return super.isCanceled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
ProgressDialog dialog = getDialog();
|
||||
if (!ApplicationManager.getApplication().isDispatchThread() || dialog == null) return;
|
||||
|
||||
JRootPane rootPane = dialog.getPanel().getRootPane();
|
||||
if (rootPane == null) {
|
||||
rootPane = considerShowingDialog(dialog);
|
||||
}
|
||||
|
||||
if (rootPane != null && timeToPaint()) {
|
||||
paintProgress(rootPane, dialog);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JRootPane considerShowingDialog(@NotNull ProgressDialog dialog) {
|
||||
if (System.currentTimeMillis() - myLastUiUpdate > DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS) {
|
||||
dialog.myRepaintRunnable.run();
|
||||
showDialog();
|
||||
return dialog.getPanel().getRootPane();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean timeToPaint() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - myLastUiUpdate <= ProgressDialog.UPDATE_INTERVAL) {
|
||||
return false;
|
||||
}
|
||||
myLastUiUpdate = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void paintProgress(@NotNull JRootPane rootPane, @NotNull ProgressDialog dialog) {
|
||||
dialog.myRepaintRunnable.run();
|
||||
|
||||
JPanel dialogPanel = dialog.getPanel();
|
||||
if (myScreenshot != null) {
|
||||
IdeGlassPaneImpl glassPane = (IdeGlassPaneImpl)IdeGlassPaneUtil.find(rootPane);
|
||||
glassPane.activateIfNeeded();
|
||||
glassPane.validate();
|
||||
|
||||
rootPane.getGraphics().drawImage(paintToBuffer(rootPane, dialogPanel), 0, 0, null);
|
||||
} else {
|
||||
dialogPanel.validate();
|
||||
dialogPanel.paintImmediately(dialogPanel.getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Image paintToBuffer(JRootPane rootPane, JPanel dialogPanel) {
|
||||
Image buffer = rootPane.createVolatileImage(rootPane.getWidth(), rootPane.getHeight());
|
||||
|
||||
Graphics g = buffer.getGraphics();
|
||||
assert myScreenshot != null;
|
||||
UIUtil.drawImage(g, myScreenshot, null, 0, 0);
|
||||
|
||||
Container container = SwingUtilities.getAncestorOfClass(DialogWrapperDialog.class, dialogPanel);
|
||||
assert container instanceof JComponent;
|
||||
g.translate(container.getX() - rootPane.getX(), container.getY() - rootPane.getY());
|
||||
container.paint(g);
|
||||
g.dispose();
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.intellij.openapi.progress.util;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.DialogWrapperPeer;
|
||||
@@ -242,7 +243,7 @@ class ProgressDialog implements Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
private static final int UPDATE_INTERVAL = 50; //msec. 20 frames per second.
|
||||
static final int UPDATE_INTERVAL = 50; //msec. 20 frames per second.
|
||||
|
||||
synchronized void update() {
|
||||
if (myRepaintedFlag) {
|
||||
@@ -280,6 +281,11 @@ class ProgressDialog implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Window getParentWindow() {
|
||||
return myParentWindow;
|
||||
}
|
||||
|
||||
void show() {
|
||||
myWasShown = true;
|
||||
if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
|
||||
@@ -299,6 +305,9 @@ class ProgressDialog implements Disposable {
|
||||
}
|
||||
if (myPopup.getPeer() instanceof DialogWrapperPeerImpl) {
|
||||
((DialogWrapperPeerImpl)myPopup.getPeer()).setAutoRequestFocus(false);
|
||||
if (ProgressIndicatorProvider.getGlobalProgressIndicator() instanceof PotemkinProgress) {
|
||||
myPopup.setModal(false); // display the dialog and continue with EDT execution, don't block it forever
|
||||
}
|
||||
}
|
||||
myPopup.pack();
|
||||
|
||||
|
||||
@@ -304,6 +304,11 @@ public class ProgressWindow extends ProgressIndicatorBase implements BlockingPro
|
||||
return myDialog != null && myDialog.getPanel() != null && myDialog.getPanel().isShowing();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected ProgressDialog getDialog() {
|
||||
return myDialog;
|
||||
}
|
||||
|
||||
public void background() {
|
||||
final Runnable backgroundHandler = myBackgroundHandler;
|
||||
if (backgroundHandler != null) {
|
||||
|
||||
@@ -483,7 +483,7 @@ public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEvent
|
||||
applyActivationState();
|
||||
}
|
||||
|
||||
private void activateIfNeeded() {
|
||||
public void activateIfNeeded() {
|
||||
if (!myPreprocessorActive && !myMouseListeners.isEmpty()) {
|
||||
myPreprocessorActive = true;
|
||||
}
|
||||
|
||||
@@ -747,6 +747,7 @@
|
||||
<action id="AddTestProcessActionIndefinte" internal="true" class="com.intellij.openapi.wm.impl.status.AddTestProcessActionIndefinite" text="Add Test Process Indefinite"/>
|
||||
<action id="AddManyTestProcesses" internal="true" class="com.intellij.openapi.wm.impl.status.AddManyTestProcesses" text="Add Many Test Processes"/>
|
||||
<action id="ShowProgressTestDialogAction" internal="true" class="com.intellij.openapi.wm.impl.status.ShowProgressTestDialogAction" text="Show Progress Test Dialog"/>
|
||||
<action id="WriteActionUnderProgress" internal="true" class="com.intellij.internal.TestWriteActionUnderProgress" text="Test Write Action Under Progress"/>
|
||||
<separator/>
|
||||
<action id="TestGestureAction" class="com.intellij.openapi.keymap.impl.ui.TestGestureAction" text="Test Gesture Action"/>
|
||||
<action id="TestDndAction" class="com.intellij.internal.validation.TestDnd" text="Test Dnd"/>
|
||||
|
||||
Reference in New Issue
Block a user