From 628589af84ea3039b1ad2dc8b7a633acc2803d3e Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Tue, 17 Mar 2015 19:41:15 +0400 Subject: [PATCH] IDEA-131544 Push dialog: be more keyboard friendly, especially in the case of Git repository with multiple remotes * change GitPushTargetPanel focus policy to cycle policy in case of multiRemote; * after popup closed in edit mode then focus force returned to edit field; * do not update target while edit mode is not finished; * restore tree selection if needed --- .../com/intellij/dvcs/push/ui/PushLog.java | 9 +++ .../src/git4idea/push/GitPushTargetPanel.java | 73 +++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java index d96dfee6ee73..4c8b86b75fef 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java @@ -113,15 +113,18 @@ public class PushLog extends JPanel implements DataProvider { if (myShouldRepaint) { refreshNode(root); } + restoreSelection(node); return result; } @Override public void cancelEditing() { + DefaultMutableTreeNode lastSelectedPathComponent = (DefaultMutableTreeNode)myTree.getLastSelectedPathComponent(); super.cancelEditing(); if (myShouldRepaint) { refreshNode(root); } + restoreSelection(lastSelectedPathComponent); } }; myTree.setUI(new MyTreeUi()); @@ -241,6 +244,12 @@ public class PushLog extends JPanel implements DataProvider { myTree.setRowHeight(0); } + private void restoreSelection(@Nullable DefaultMutableTreeNode node) { + if (node != null) { + TreeUtil.selectNode(myTree, node); + } + } + private JComponent createStrategyPanel() { final JPanel labelPanel = new JPanel(new BorderLayout()); labelPanel.setBackground(myTree.getBackground()); diff --git a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java index b33ed23537f5..932e5c38736d 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java @@ -27,18 +27,20 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.ValidationInfo; -import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.components.JBLabel; +import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.table.ComponentsListFocusTraversalPolicy; import git4idea.GitRemoteBranch; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; @@ -51,6 +53,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.text.ParseException; import java.util.Comparator; @@ -91,7 +96,10 @@ public class GitPushTargetPanel extends PushTargetPanel { showDefineRemoteDialog(); } else { - showRemoteSelector(event); + Component eventComponent = event.getComponent(); + if (eventComponent != null) { + showRemoteSelector(eventComponent, event.getPoint()); + } } } }); @@ -107,6 +115,21 @@ public class GitPushTargetPanel extends PushTargetPanel { add(myTargetEditor, BorderLayout.CENTER); updateComponents(defaultTarget); + + setFocusCycleRoot(true); + setFocusTraversalPolicyProvider(true); + myRemoteRenderer.setFocusable(true); + myTargetEditor.setFocusable(true); + setFocusTraversalPolicy(new MyGitTargetFocusTraversalPolicy()); + myRemoteRenderer.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + // show in edit mode only + if (myTargetEditor.isShowing()) { + showRemoteSelector(myRemoteRenderer, new Point(myRemoteRenderer.getLocation())); + } + } + }); } private void updateComponents(@Nullable GitPushTarget target) { @@ -205,23 +228,33 @@ public class GitPushTargetPanel extends PushTargetPanel { }); } - private void showRemoteSelector(@NotNull MouseEvent event) { + private void showRemoteSelector(@NotNull Component component, @NotNull Point point) { final List remotes = getRemotes(); if (remotes.size() <= 1) { return; } - - ListPopup popup = JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep(null, remotes) { + ListPopup popup = new ListPopupImpl(new BaseListPopupStep(null, remotes) { @Override public PopupStep onChosen(String selectedValue, boolean finalChoice) { myRemoteRenderer.updateLinkText(selectedValue); - if (myFireOnChangeAction != null) { + if (myFireOnChangeAction != null && !myTargetEditor.isShowing()) { + //fireOnChange only when editing completed myFireOnChangeAction.run(); } return super.onChosen(selectedValue, finalChoice); } - }); - popup.show(new RelativePoint(event)); + }) { + @Override + public void cancel(InputEvent e) { + super.cancel(e); + if (myTargetEditor.isShowing()) { + //repaint and force move focus to target editor component + GitPushTargetPanel.this.repaint(); + IdeFocusManager.getInstance(myProject).requestFocus(myTargetEditor, true); + } + } + }; + popup.show(new RelativePoint(component, point)); } @NotNull @@ -363,4 +396,28 @@ public class GitPushTargetPanel extends PushTargetPanel { } }); } + + private class MyGitTargetFocusTraversalPolicy extends ComponentsListFocusTraversalPolicy { + @NotNull + @Override + protected List getOrderedComponents() { + return ContainerUtil.newArrayList(myTargetEditor.getFocusTarget(), myRemoteRenderer); + } + + @Override + public Component getComponentAfter(Container aContainer, Component aComponent) { + if (getRemotes().size() > 1) { + return super.getComponentAfter(aContainer, aComponent); + } + return aComponent; + } + + @Override + public Component getComponentBefore(Container aContainer, Component aComponent) { + if (getRemotes().size() > 1) { + return super.getComponentBefore(aContainer, aComponent); + } + return aComponent; + } + } }