synchronous toolwindows initializing to prevent re-layout during startup

This commit is contained in:
Dmitry Avdeev
2016-09-20 14:41:33 +03:00
parent 9d8cd9029a
commit 3afe8c2c8f
3 changed files with 83 additions and 82 deletions
@@ -15,13 +15,9 @@
*/
package com.intellij.openapi.wm.impl;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.impl.commands.FinalizableCommand;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,7 +40,7 @@ public final class CommandProcessor implements Runnable {
/**
* Executes passed batch of commands. Note, that the processor surround the
* commands with BlockFocusEventsCmd - UnbockFocusEventsCmd. It's required to
* commands with BlockFocusEventsCmd - UnblockFocusEventsCmd. It's required to
* prevent focus handling of events which is caused by the commands to be executed.
*/
public final void execute(@NotNull List<FinalizableCommand> commandList, @NotNull Condition expired) {
@@ -63,32 +59,27 @@ public final class CommandProcessor implements Runnable {
public final void run() {
synchronized (myLock) {
final CommandGroup commandGroup = getNextCommandGroup();
if (commandGroup == null || commandGroup.isEmpty()) return;
final Condition conditionForGroup = commandGroup.getExpireCondition();
final FinalizableCommand command = commandGroup.takeNextCommand();
myCommandCount--;
final Condition expire = command.getExpireCondition() != null ? command.getExpireCondition() : conditionForGroup;
if (LOG.isDebugEnabled()) {
LOG.debug("CommandProcessor.run " + command);
//noinspection StatementWithEmptyBody
while (runNext()) {
}
// max. I'm not actually quite sure this should have NON_MODAL modality but it should
// definitely have some since runnables in command list may (and do) request some PSI activity
final boolean queueNext = myCommandCount > 0;
Application application = ApplicationManager.getApplication();
ModalityState modalityState = Registry.is("ide.perProjectModality") ? ModalityState.current() : ModalityState.NON_MODAL;
application.getInvokator().invokeLater(command, modalityState, expire == null ? application.getDisposed() : expire).doWhenDone(() -> {
if (queueNext) {
this.run();
}
});
}
}
private boolean runNext() {
final CommandGroup commandGroup = getNextCommandGroup();
if (commandGroup == null || commandGroup.isEmpty()) return false;
final FinalizableCommand command = commandGroup.takeNextCommand();
myCommandCount--;
if (LOG.isDebugEnabled()) {
LOG.debug("CommandProcessor.run " + command);
}
command.run();
return true;
}
@Nullable
private CommandGroup getNextCommandGroup() {
while (!myCommandGroupList.isEmpty()) {
@@ -37,14 +37,11 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.progress.util.ReadTask;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Splitter;
@@ -62,7 +59,9 @@ import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.switcher.QuickAccessSettings;
import com.intellij.ui.switcher.SwitchManager;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.EdtInvocationManager;
import com.intellij.util.ui.PositionTracker;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.UiNotifyConnector;
@@ -100,6 +99,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
private final Map<String, WindowedDecorator> myId2WindowedDecorator = new HashMap<>();
private final Map<String, StripeButton> myId2StripeButton = new HashMap<>();
private final Map<String, FocusWatcher> myId2FocusWatcher = new HashMap<>();
private final Set<String> myDumbAwareIds = Collections.synchronizedSet(ContainerUtil.<String>newTroveSet());
private final EditorComponentFocusWatcher myEditorComponentFocusWatcher = new EditorComponentFocusWatcher();
private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener = new MyToolWindowPropertyChangeListener();
@@ -380,20 +380,22 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
myToolWindowsPane = new ToolWindowsPane(myFrame, this);
Disposer.register(myProject, myToolWindowsPane);
((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(myToolWindowsPane);
List<FinalizableCommand> commandsList = new ArrayList<>();
appendUpdateToolWindowsPaneCmd(commandsList);
myFrame.setTitle(FrameTitleBuilder.getInstance().getProjectTitle(myProject));
JComponent editorComponent = createEditorComponent(myProject);
myEditorComponentFocusWatcher.install(editorComponent);
appendSetEditorComponentCmd(editorComponent, commandsList);
if (myEditorWasActive && editorComponent instanceof EditorsSplitters) {
activateEditorComponentImpl(commandsList, true);
}
execute(commandsList);
final DumbService.DumbModeListener dumbModeListener = new DumbService.DumbModeListener() {
@Override
public void enteredDumbMode() {
disableStripeButtons();
}
StartupManager.getInstance(myProject).registerPostStartupActivity((DumbAwareRunnable)() -> registerToolWindowsFromBeans());
@Override
public void exitDumbMode() {
for (final String id : getToolWindowIds()) {
getStripeButton(id).setEnabled(true);
}
}
};
myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
IdeEventQueue.getInstance().addDispatcher(e -> {
if (e instanceof KeyEvent) {
@@ -406,51 +408,46 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}, myProject);
}
private void initAll() {
List<FinalizableCommand> commandsList = new ArrayList<>();
appendUpdateToolWindowsPaneCmd(commandsList);
JComponent editorComponent = createEditorComponent(myProject);
myEditorComponentFocusWatcher.install(editorComponent);
appendSetEditorComponentCmd(editorComponent, commandsList);
if (myEditorWasActive && editorComponent instanceof EditorsSplitters) {
activateEditorComponentImpl(commandsList, true);
}
execute(commandsList);
}
private void disableStripeButtons() {
for (final String id : getToolWindowIds()) {
if (!myDumbAwareIds.contains(id)) {
if (isToolWindowVisible(id)) {
hideToolWindow(id, true);
}
StripeButton button = getStripeButton(id);
if (button != null) {
button.setEnabled(false);
}
}
}
}
private static JComponent createEditorComponent(@NotNull Project project) {
return FrameEditorComponentProvider.EP.getExtensions()[0].createEditorComponent(project);
}
private void registerToolWindowsFromBeans() {
List<ToolWindowEP> beans = new ArrayList<>(Arrays.asList(Extensions.getExtensions(ToolWindowEP.EP_NAME)));
Collections.reverse(beans);
checkConditionsInReadAction(beans, new ArrayList<>());
}
private void checkConditionsInReadAction(@NotNull List<ToolWindowEP> beans, @NotNull List<ToolWindowEP> checkedSuccessfully) {
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ReadTask() {
@Nullable
@Override
public Continuation performInReadAction(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
for (int i = beans.size() - 1; i >= 0; i--) {
indicator.checkCanceled();
ToolWindowEP bean = beans.remove(i);
Condition<Project> condition = ObjectUtils.notNull(bean.getCondition(), Conditions.<Project>alwaysTrue());
if (!myProject.isDisposed() && condition.value(myProject)) {
checkedSuccessfully.add(bean);
}
}
return new Continuation(() -> {
if (!myProject.isDisposed()) {
for (ToolWindowEP bean : checkedSuccessfully) {
if (getToolWindow(bean.id) == null) {
try {
initToolWindow(bean);
}
catch (Throwable e) {
LOG.error(String.format("Tool window %s initialization failed", bean.id), e);
}
}
}
}
}, ModalityState.any());
List<ToolWindowEP> beans = Arrays.asList(Extensions.getExtensions(ToolWindowEP.EP_NAME));
for (ToolWindowEP bean : beans) {
Condition<Project> condition = bean.getCondition();
if (condition == null || condition.value(myProject)) {
EdtInvocationManager.getInstance().invokeLater(() -> initToolWindow(bean));
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
checkConditionsInReadAction(beans, checkedSuccessfully);
}
});
}
}
@Override
@@ -2454,4 +2451,16 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
WindowInfoImpl info = getInfo(id);
return info == null || info.isShowStripeButton();
}
public static class InitToolWindowsActivity implements StartupActivity, DumbAware {
@Override
public void runActivity(@NotNull Project project) {
ToolWindowManagerEx ex = ToolWindowManagerEx.getInstanceEx(project);
if (ex instanceof ToolWindowManagerImpl) {
ToolWindowManagerImpl myManager = (ToolWindowManagerImpl)ex;
myManager.registerToolWindowsFromBeans();
EdtInvocationManager.getInstance().invokeLater(() -> myManager.initAll());
}
}
}
}
@@ -375,6 +375,7 @@
<postStartupActivity implementation="com.intellij.ide.TipOfTheDayManager"/>
<postStartupActivity implementation="com.intellij.ide.actions.PowerSaveModeNotifier"/>
<postStartupActivity implementation="com.intellij.openapi.wm.impl.ToolWindowManagerImpl$InitToolWindowsActivity" order="after OpenFilesActivity"/>
<postStartupActivity id="OpenFilesActivity" implementation="com.intellij.openapi.fileEditor.impl.OpenFilesActivity"/>
<postStartupActivity implementation="com.intellij.xml.breadcrumbs.BreadcrumbsInitializingActivity"/>