Merge remote-tracking branch 'origin/valich/single_switcher'

This commit is contained in:
Valentin Fondaratov
2019-02-06 13:35:53 +03:00
6 changed files with 219 additions and 88 deletions
@@ -81,7 +81,7 @@ public class RecentLocationsAction extends AnAction {
private static final int MINIMUM_WIDTH = JBUI.scale(200);
private static final int MINIMUM_HEIGHT = JBUI.scale(100);
private static final Color SHORTCUT_FOREGROUND_COLOR = UIUtil.getContextHelpForeground();
private static final String SHORTCUT_HEX_COLOR = String.format("#%02x%02x%02x",
public static final String SHORTCUT_HEX_COLOR = String.format("#%02x%02x%02x",
SHORTCUT_FOREGROUND_COLOR.getRed(),
SHORTCUT_FOREGROUND_COLOR.getGreen(),
SHORTCUT_FOREGROUND_COLOR.getBlue());
@@ -22,6 +22,7 @@ package com.intellij.ide.actions;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.project.DumbAwareAction;
import org.jetbrains.annotations.NotNull;
@@ -32,7 +33,7 @@ public class ShowRecentFilesAction extends DumbAwareAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.recent.files");
Switcher.createAndShowSwitcher(e, IdeBundle.message("title.popup.recent.files"), true, null);
Switcher.createAndShowSwitcher(e, IdeBundle.message("title.popup.recent.files"), IdeActions.ACTION_RECENT_FILES,false, true);
}
@Override
@@ -20,7 +20,6 @@
package com.intellij.ide.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
@@ -30,7 +29,7 @@ public class ShowRecentlyEditedFilesAction extends DumbAwareAction {
public void actionPerformed(@NotNull AnActionEvent e) {
final Project project = e.getProject();
if (project != null) {
Switcher.createAndShowSwitcher(e, "Recently Edited Files", true, IdeDocumentHistory.getInstance(project).getChangedFiles());
Switcher.createAndShowSwitcher(e, "Recently Edited Files", "RecentChangedFiles", true, true);
}
}
@@ -3,18 +3,21 @@ package com.intellij.ide.actions;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.UISettingsState;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.PresentationFactory;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Experiments;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.markup.EffectType;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
import com.intellij.openapi.fileEditor.impl.EditorTabPresentationUtil;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
@@ -43,6 +46,8 @@ import com.intellij.openapi.wm.impl.ToolWindowManagerImpl;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.ui.*;
import com.intellij.ui.border.CustomLineBorder;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.ui.speedSearch.NameFilteringListModel;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
@@ -66,6 +71,7 @@ import java.io.File;
import java.util.List;
import java.util.*;
import static com.intellij.ide.actions.RecentLocationsAction.SHORTCUT_HEX_COLOR;
import static com.intellij.openapi.keymap.KeymapUtil.getActiveKeymapShortcuts;
import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
import static java.awt.event.KeyEvent.*;
@@ -146,7 +152,8 @@ public class Switcher extends AnAction implements DumbAware {
}
if (SWITCHER == null) {
isNewSwitcher = true;
SWITCHER = createAndShowSwitcher(e, SWITCHER_TITLE, false, null);
// Assigns SWITCHER field
createAndShowSwitcher(project, SWITCHER_TITLE, IdeActions.ACTION_SWITCHER, false, false);
FeatureUsageTracker.getInstance().triggerFeatureUsed(SWITCHER_FEATURE_ID);
}
}
@@ -167,32 +174,47 @@ public class Switcher extends AnAction implements DumbAware {
}
}
/**
* @deprecated Please use {@link Switcher#createAndShowSwitcher(AnActionEvent, String, boolean, boolean)}
*/
@Deprecated
@Nullable
public static SwitcherPanel createAndShowSwitcher(@NotNull AnActionEvent e, @NotNull String title, boolean pinned, @Nullable final VirtualFile[] vFiles) {
Project project = getEventProject(e);
if (SWITCHER != null && Comparing.equal(SWITCHER.myTitle, title)) {
SWITCHER.goForward();
return null;
return createAndShowSwitcher(e, title, "RecentFiles", pinned, vFiles != null);
}
public static SwitcherPanel createAndShowSwitcher(@NotNull AnActionEvent e, @NotNull String title, @NotNull String actionId, boolean onlyEdited, boolean pinned) {
Project project = e.getProject();
if (SWITCHER != null) {
final boolean sameShortcut = Comparing.equal(SWITCHER.myTitle, title);
if (SWITCHER.isCheckboxMode()) {
if (sameShortcut) {
SWITCHER.toggleShowEditedFiles();
}
else {
SWITCHER.setShowOnlyEditedFiles(onlyEdited);
}
return null;
}
else if (sameShortcut) {
SWITCHER.goForward();
return null;
}
}
return project == null ? null : createAndShowSwitcher(project, title, pinned, vFiles == null ? null : Arrays.asList(vFiles));
return project == null ? null : createAndShowSwitcher(project, title, actionId, onlyEdited, pinned);
}
@Nullable
private static SwitcherPanel createAndShowSwitcher(@NotNull Project project,
@NotNull String title,
boolean pinned,
@Nullable final List<VirtualFile> vFiles) {
@NotNull String actionId,
boolean onlyEdited,
boolean pinned) {
synchronized (Switcher.class) {
if (SWITCHER != null) {
SWITCHER.cancel();
}
SWITCHER = new SwitcherPanel(project, title, pinned) {
@NotNull
@Override
protected List<VirtualFile> getFiles(@NotNull Project project) {
return vFiles != null ? vFiles : super.getFiles(project);
}
};
SWITCHER = new SwitcherPanel(project, title, actionId, onlyEdited, pinned);
return SWITCHER;
}
}
@@ -203,6 +225,7 @@ public class Switcher extends AnAction implements DumbAware {
final JBList files;
final JPanel separator;
final ToolWindowManager twManager;
final JBCheckBox myShowOnlyEditedFilesCheckBox;
final JLabel pathLabel = new JLabel(" ");
final JPanel descriptions;
final Project project;
@@ -211,6 +234,7 @@ public class Switcher extends AnAction implements DumbAware {
final Alarm myAlarm;
final SwitcherSpeedSearch mySpeedSearch;
final String myTitle;
final String myActionId;
@Nullable
@Override
@@ -301,10 +325,11 @@ public class Switcher extends AnAction implements DumbAware {
};
@SuppressWarnings({"ConstantConditions"})
SwitcherPanel(@NotNull final Project project, @NotNull String title, boolean pinned) {
SwitcherPanel(@NotNull final Project project, @NotNull String title, @NotNull String actionId, boolean onlyEdited, boolean pinned) {
setLayout(new SwitcherLayouter());
this.project = project;
myTitle = title;
myActionId = actionId;
myPinned = pinned;
mySpeedSearch = pinned ? new SwitcherSpeedSearch() : null;
@@ -327,7 +352,7 @@ public class Switcher extends AnAction implements DumbAware {
descriptions.setBorder(new CustomLineBorder(JBUI.CurrentTheme.Advertiser.borderColor(), JBUI.insetsTop(1)));
descriptions.add(pathLabel, BorderLayout.CENTER);
twManager = ToolWindowManager.getInstance(project);
DefaultListModel twModel = new DefaultListModel();
CollectionListModel<ToolWindow> twModel = new CollectionListModel<ToolWindow>();
List<ActivateToolWindowAction> actions = ToolWindowsGroup.getToolWindowActions(project, true);
List<ToolWindow> windows = ContainerUtil.newArrayList();
for (ActivateToolWindowAction action : actions) {
@@ -340,7 +365,7 @@ public class Switcher extends AnAction implements DumbAware {
final Map<ToolWindow, String> map = ContainerUtil.reverseMap(twShortcuts);
Collections.sort(windows, (o1, o2) -> StringUtil.compare(map.get(o1), map.get(o2), false));
for (ToolWindow window : windows) {
twModel.addElement(window);
twModel.add(window);
}
toolWindows = new JBList(twModel);
@@ -390,66 +415,12 @@ public class Switcher extends AnAction implements DumbAware {
separator.setPreferredSize(JBUI.size(9, 10));
separator.setBackground(toolWindows.getBackground());
int selectionIndex = -1;
final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project);
final ArrayList<FileInfo> filesData = new ArrayList<>();
final ArrayList<FileInfo> editors = new ArrayList<>();
if (!pinned) {
if (UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE) {
for (Pair<VirtualFile, EditorWindow> pair : editorManager.getSelectionHistory()) {
editors.add(new FileInfo(pair.first, pair.second, project));
}
}
}
if (editors.size() < 2 || isPinnedMode()) {
if (isPinnedMode() && editors.size() > 1) {
filesData.addAll(editors);
}
final List<VirtualFile> recentFiles = getFiles(project);
final int maxFiles = Math.max(editors.size(), recentFiles.size());
final int minIndex = isPinnedMode() ? 0 : (recentFiles.size() - Math.min(toolWindows.getModel().getSize(), maxFiles));
boolean firstRecentMarked = false;
final List<VirtualFile> selectedFiles = Arrays.asList(editorManager.getSelectedFiles());
for (int i = recentFiles.size() - 1; i >= minIndex; i--) {
if (isPinnedMode()
&& selectedFiles.contains(recentFiles.get(i))
&& UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE) {
continue;
}
final FileInfo info = new FileInfo(recentFiles.get(i), null, project);
boolean add = true;
if (isPinnedMode()) {
for (FileInfo fileInfo : filesData) {
if (fileInfo.first.equals(info.first)) {
add = false;
break;
}
}
}
if (add) {
filesData.add(info);
if (!firstRecentMarked) {
selectionIndex = filesData.size() - 1;
if (selectionIndex != 0 || UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE || !isPinnedMode() || selectedFiles.isEmpty()) {
firstRecentMarked = true;
}
}
}
}
//if (editors.size() == 1) selectionIndex++;
if (editors.size() == 1 && (filesData.isEmpty() || !editors.get(0).getFirst().equals(filesData.get(0).getFirst()))) {
filesData.add(0, editors.get(0));
}
} else {
for (int i = 0; i < Math.min(30, editors.size()); i++) {
filesData.add(editors.get(i));
}
}
final DefaultListModel filesModel = new DefaultListModel();
for (FileInfo editor : filesData) {
filesModel.addElement(editor);
final Pair<List<FileInfo>, Integer> filesAndSelection = getFilesToShowAndSelectionIndex(project, collectFiles(project, onlyEdited),
toolWindows.getModel().getSize(), pinned);
final int selectionIndex = filesAndSelection.getSecond();
final CollectionListModel<FileInfo> filesModel = new CollectionListModel<FileInfo>();
for (FileInfo editor : filesAndSelection.getFirst()) {
filesModel.add(editor);
}
final VirtualFilesRenderer filesRenderer = new VirtualFilesRenderer(this) {
@@ -557,7 +528,7 @@ public class Switcher extends AnAction implements DumbAware {
ScrollingUtil.ensureSelectionExists(files);
this.add(toolWindows, BorderLayout.WEST);
if (filesModel.size() > 0) {
if (filesModel.getSize() > 0) {
files.setAlignmentY(1f);
final JScrollPane pane = ScrollPaneFactory.createScrollPane(files, true);
pane.setPreferredSize(new Dimension(files.getPreferredSize().width, 20 * 20));
@@ -580,13 +551,31 @@ public class Switcher extends AnAction implements DumbAware {
KeymapUtil.reassignAction(files, getKeyStroke(VK_UP, 0), getKeyStroke(VK_UP, CTRL_DOWN_MASK), WHEN_FOCUSED, false);
KeymapUtil.reassignAction(files, getKeyStroke(VK_DOWN, 0), getKeyStroke(VK_DOWN, CTRL_DOWN_MASK), WHEN_FOCUSED, false);
myShowOnlyEditedFilesCheckBox = new MyCheckBox(actionId, onlyEdited);
JPanel topPanel = createTopPanel(myShowOnlyEditedFilesCheckBox, isCheckboxMode() ? IdeBundle.message("title.popup.recent.files") : title);
this.add(topPanel, BorderLayout.NORTH);
if (isCheckboxMode()) {
myShowOnlyEditedFilesCheckBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setShowOnlyEditedFiles(myShowOnlyEditedFilesCheckBox.isSelected());
}
});
}
else {
myShowOnlyEditedFilesCheckBox.setEnabled(false);
myShowOnlyEditedFilesCheckBox.setVisible(false);
}
myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(this, filesModel.getSize() > 0 ? files : toolWindows)
.setResizable(pinned)
.setModalContext(false)
.setFocusable(true)
.setRequestFocus(true)
.setTitle(title)
.setCancelOnWindowDeactivation(true)
.setCancelOnOtherWindowOpen(true)
.setMovable(pinned)
@@ -637,7 +626,6 @@ public class Switcher extends AnAction implements DumbAware {
addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, "LEFT");
addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, "control RIGHT");
addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, "control LEFT");
}
private Container getPopupFocusAncestor() {
@@ -645,6 +633,92 @@ public class Switcher extends AnAction implements DumbAware {
return content == null ? null : content.getFocusCycleRootAncestor();
}
@NotNull
private static List<VirtualFile> collectFiles(@NotNull Project project, boolean onlyEdited) {
return onlyEdited ? Arrays.asList(IdeDocumentHistory.getInstance(project).getChangedFiles())
: EditorHistoryManager.getInstance(project).getFileList();
}
@NotNull
private static Pair<List<FileInfo>, Integer> getFilesToShowAndSelectionIndex(@NotNull Project project,
@NotNull List<VirtualFile> filesForInit,
int toolWindowsCount,
boolean pinned) {
int selectionIndex = -1;
final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project);
final ArrayList<FileInfo> filesData = new ArrayList<>();
final ArrayList<FileInfo> editors = new ArrayList<>();
if (!pinned) {
if (UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE) {
for (Pair<VirtualFile, EditorWindow> pair : editorManager.getSelectionHistory()) {
editors.add(new FileInfo(pair.first, pair.second, project));
}
}
}
if (editors.size() < 2 || pinned) {
if (pinned && editors.size() > 1) {
filesData.addAll(editors);
}
final List<VirtualFile> recentFiles = filesForInit;
final int maxFiles = Math.max(editors.size(), recentFiles.size());
final int minIndex = pinned ? 0 : (recentFiles.size() - Math.min(toolWindowsCount, maxFiles));
boolean firstRecentMarked = false;
final List<VirtualFile> selectedFiles = Arrays.asList(editorManager.getSelectedFiles());
for (int i = recentFiles.size() - 1; i >= minIndex; i--) {
if (pinned
&& selectedFiles.contains(recentFiles.get(i))
&& UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE) {
continue;
}
final FileInfo info = new FileInfo(recentFiles.get(i), null, project);
boolean add = true;
if (pinned) {
for (FileInfo fileInfo : filesData) {
if (fileInfo.first.equals(info.first)) {
add = false;
break;
}
}
}
if (add) {
filesData.add(info);
if (!firstRecentMarked) {
selectionIndex = filesData.size() - 1;
if (selectionIndex != 0 || UISettings.getInstance().getEditorTabPlacement() != UISettings.TABS_NONE || !pinned || selectedFiles.isEmpty()) {
firstRecentMarked = true;
}
}
}
}
//if (editors.size() == 1) selectionIndex++;
if (editors.size() == 1 && (filesData.isEmpty() || !editors.get(0).getFirst().equals(filesData.get(0).getFirst()))) {
filesData.add(0, editors.get(0));
}
} else {
for (int i = 0; i < Math.min(30, editors.size()); i++) {
filesData.add(editors.get(i));
}
}
return Pair.create(filesData, selectionIndex);
}
@NotNull
private static JPanel createTopPanel(JBCheckBox showOnlyEditedFilesCheckBox, @NotNull String title) {
JPanel topPanel = new CaptionPanel();
JBLabel titleLabel = new JBLabel(title);
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
topPanel.add(titleLabel, BorderLayout.WEST);
topPanel.add(showOnlyEditedFilesCheckBox, BorderLayout.EAST);
Dimension size = topPanel.getPreferredSize();
size.height = JBUI.scale(29);
topPanel.setPreferredSize(size);
topPanel.setBorder(JBUI.Borders.empty(5, 8));
return topPanel;
}
private static void addFocusTraversalKeys (Container focusCycleRoot, int focusTraversalType, String keyStroke) {
Set<AWTKeyStroke> focusTraversalKeySet = focusCycleRoot.getFocusTraversalKeys(focusTraversalType);
@@ -653,9 +727,10 @@ public class Switcher extends AnAction implements DumbAware {
focusCycleRoot.setFocusTraversalKeys(focusTraversalType, set);
}
@Deprecated
@NotNull
protected List<VirtualFile> getFiles(@NotNull Project project) {
return EditorHistoryManager.getInstance(project).getFileList();
throw new UnsupportedOperationException("deprecated");
}
@NotNull
@@ -817,8 +892,8 @@ public class Switcher extends AnAction implements DumbAware {
private static void removeElementAt(@NotNull JList jList, int index) {
final ListModel model = jList.getModel();
if (model instanceof DefaultListModel) {
((DefaultListModel)model).removeElementAt(index);
if (model instanceof CollectionListModel) {
((CollectionListModel)model).remove(index);
}
else if (model instanceof NameFilteringListModel) {
((NameFilteringListModel)model).remove(index);
@@ -891,6 +966,40 @@ public class Switcher extends AnAction implements DumbAware {
return files.hasFocus() ? files : toolWindows.hasFocus() ? toolWindows : preferable;
}
boolean isCheckboxMode() {
return isPinnedMode() && Experiments.isFeatureEnabled("recent.and.edited.files.together");
}
void toggleShowEditedFiles() {
myShowOnlyEditedFilesCheckBox.doClick();
}
void setShowOnlyEditedFiles(boolean onlyEdited) {
if (myShowOnlyEditedFilesCheckBox.isSelected() != onlyEdited) {
myShowOnlyEditedFilesCheckBox.setSelected(onlyEdited);
}
final boolean listWasSelected = files.getSelectedIndex() != -1;
final Pair<List<FileInfo>, Integer> filesAndSelection = getFilesToShowAndSelectionIndex(
project, collectFiles(project, onlyEdited), toolWindows.getModel().getSize(), isPinnedMode());
final int selectionIndex = filesAndSelection.getSecond();
final ListModel model = files.getModel();
if (model instanceof CollectionListModel) {
((CollectionListModel)model).replaceAll(filesAndSelection.getFirst());
}
else if (model instanceof NameFilteringListModel) {
((NameFilteringListModel)model).replaceAll(filesAndSelection.getFirst());
}
if (selectionIndex > -1 && listWasSelected) {
files.setSelectedIndex(selectionIndex);
}
files.revalidate();
files.repaint();
}
void navigate(final InputEvent e) {
final boolean openInNewWindow = e != null && e.isShiftDown() && e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == VK_ENTER;
final Object[] values = getSelectedList().getSelectedValues();
@@ -1178,6 +1287,23 @@ public class Switcher extends AnAction implements DumbAware {
}
}
private static class MyCheckBox extends JBCheckBox {
private MyCheckBox(@NotNull String actionId, boolean selected) {
super(layoutText(actionId), selected);
setOpaque(false);
setFocusable(false);
}
private static String layoutText(@NotNull String actionId) {
ShortcutSet shortcuts = KeymapUtil.getActiveKeymapShortcuts(actionId);
return "<html>"
+ IdeBundle.message("recent.files.checkbox.label")
+ " <font color=\"" + SHORTCUT_HEX_COLOR + "\">"
+ KeymapUtil.getShortcutsText(shortcuts.getShortcuts()) + "</font>"
+ "</html>";
}
}
private static class VirtualFilesRenderer extends ColoredListCellRenderer {
private final SwitcherPanel mySwitcherPanel;
boolean open;
@@ -473,6 +473,7 @@ command.select.all=Select All
message.no.targets.available=No targets available in this context
title.popup.select.target=Select In
title.popup.recent.files=Recent Files
recent.files.checkbox.label=Show changed only
recent.locations.popup.title=Recent Locations
recent.locations.changed.locations=Recent Changed Locations
recent.locations.popup.empty.text=No recent locations found
@@ -594,6 +594,10 @@
<description>Intergation with global menu in Linux</description>
</experimentalFeature>
<experimentalFeature id="recent.and.edited.files.together" percentOfUsers="0">
<description>Second Cmd/Ctrl+E switches to Recently Edited Files</description>
</experimentalFeature>
<rawEditorTypedHandler implementationClass="com.intellij.openapi.editor.impl.EditorFactoryImpl$MyRawTypedHandler"/>
</extensions>
</idea-plugin>