IDEA-61356 Running an application with opened editor: focus is inconsistent

This commit is contained in:
Kirill Kalishev
2010-11-17 20:51:50 +03:00
parent 6ab68a4da9
commit a6159adb03
4 changed files with 132 additions and 91 deletions
@@ -28,7 +28,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
public final class InplaceButton extends JComponent implements ActiveComponent {
public class InplaceButton extends JComponent implements ActiveComponent {
private boolean myPainting = true;
private boolean myActive = true;
@@ -43,6 +43,8 @@ public final class InplaceButton extends JComponent implements ActiveComponent {
private int myYTransform = 0;
private boolean myFill;
private boolean myHoveringEnabled;
public InplaceButton(String tooltip, final Icon icon, final ActionListener listener) {
this(new IconButton(tooltip, icon, icon), listener, null);
}
@@ -142,7 +144,7 @@ public final class InplaceButton extends JComponent implements ActiveComponent {
g.translate(myXTransform, myYTransform);
if (myBehavior.isHovered()) {
if (myBehavior.isHovered() && myHoveringEnabled) {
if (myBehavior.isPressedByMouse()) {
myHovered.paintIcon(this, g, 1, 1);
}
@@ -151,7 +153,7 @@ public final class InplaceButton extends JComponent implements ActiveComponent {
}
}
else {
if (myActive) {
if (isActive()) {
myRegular.paintIcon(this, g, 0, 0);
}
else {
@@ -167,4 +169,13 @@ public final class InplaceButton extends JComponent implements ActiveComponent {
myYTransform = y;
}
public void setHoveringEnabled(boolean enabled) {
myHoveringEnabled = enabled;
}
public boolean isActive() {
return myActive;
}
}
@@ -101,11 +101,8 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
private final MyDivider myDivider;
private final TitlePanel myTitlePanel;
private final MyTitleButton myToggleFloatingModeButton;
private final Separator myFloatingDockSeparator;
private final MyTitleButton myToggleDockModeButton;
private final Separator myDockAutoHideSeparator;
private final MyTitleButton myToggleAutoHideModeButton;
private final Separator myAutoHideHideSeparator;
private final MyTitleButton myHideButton;
private final EventListenerList myListenerList;
/*
@@ -139,17 +136,18 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
myToolWindow = toolWindow;
myToolWindow.setDecorator(this);
myDivider = new MyDivider();
myTitlePanel = new TitlePanel();
myTitlePanel = new TitlePanel() {
@Override
public boolean isActive() {
return computeActive();
}
};
myTitleTabs = toolWindow.getContentUI().getTabComponent();
myToggleFloatingModeAction = new ToggleFloatingModeAction();
myToggleSideModeAction = new ToggleSideModeAction();
myFloatingDockSeparator = new Separator();
myFloatingDockSeparator.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
myToggleDockModeAction = new ToggleDockModeAction();
myDockAutoHideSeparator = new Separator();
myToggleAutoHideModeAction = new TogglePinnedModeAction();
myAutoHideHideSeparator = new Separator();
myHideAction = new HideAction();
HideSideAction hideSideAction = new HideSideAction();
myToggleContentUiTypeAction = new ToggleContentUiTypeAction();
@@ -177,6 +175,14 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
apply(info);
}
private boolean computeActive() {
Component component = IdeFocusManager.getInstance(myProject).getFocusedDescendantFor(myToolWindow.getComponent());
if (component != null) return true;
Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
return owner != null && SwingUtilities.isDescendingFrom(owner, myToolWindow.getComponent());
}
/**
* Applies specified decoration.
*/
@@ -185,14 +191,6 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
return;
}
myInfo = info;
// Active
final boolean active = info.isActive();
myTitlePanel.setActive(active);
//todo myToolWindowBorder.setActive(active);
myFloatingDockSeparator.setActive(active);
myDockAutoHideSeparator.setActive(active);
myAutoHideHideSeparator.setActive(active);
// Anchor
final ToolWindowAnchor anchor = myInfo.getAnchor();
@@ -220,16 +218,16 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
if (!info.isFloating()) {
myHideSideButton.setVisible(true);
if (ToolWindowAnchor.TOP == anchor) {
myHideSideButton.setIcon(active ? ourHideSideUp : ourHideSideUpInactive);
myHideSideButton.setIcon(ourHideSideUp, ourHideSideUpInactive);
}
else if (ToolWindowAnchor.LEFT == anchor) {
myHideSideButton.setIcon(active ? ourHideSideLeft : ourHideSideLeftInactive);
myHideSideButton.setIcon(ourHideSideLeft, ourHideSideLeftInactive);
}
else if (ToolWindowAnchor.BOTTOM == anchor) {
myHideSideButton.setIcon(active ? ourHideSideDown : ourHideSideDownInactive);
myHideSideButton.setIcon(ourHideSideDown, ourHideSideDownInactive);
}
else if (ToolWindowAnchor.RIGHT == anchor) {
myHideSideButton.setIcon(active ? ourHideSideRight : ourHideSideRightInactive);
myHideSideButton.setIcon(ourHideSideRight, ourHideSideRightInactive);
}
}
else {
@@ -240,38 +238,30 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
repaint();
// Type
if (myInfo.isDocked()) {
myToggleFloatingModeButton.setIcon(active ? ourFloatingIcon : ourFloatingInactiveIcon);
myToggleFloatingModeButton.setIcon(ourFloatingIcon, ourFloatingInactiveIcon);
myToggleDockModeButton.setVisible(true);
myDockAutoHideSeparator.setVisible(true);
myToggleDockModeButton.setIcon(active ? ourSlidingIcon : ourSlidingInactiveIcon);
myToggleDockModeButton.setIcon(ourSlidingIcon, ourSlidingInactiveIcon);
myToggleAutoHideModeButton.setVisible(true);
myAutoHideHideSeparator.setVisible(true);
myToggleAutoHideModeButton.setIcon(active
? (myInfo.isAutoHide() ? ourAuthoHideOnIcon : ourAuthoHideOffIcon)
: (myInfo.isAutoHide() ? ourAuthoHideOnInactiveIcon : ourAuthoHideOffInactiveIcon));
myToggleAutoHideModeButton.setIcon((myInfo.isAutoHide() ? ourAuthoHideOnIcon : ourAuthoHideOffIcon),
(myInfo.isAutoHide() ? ourAuthoHideOnInactiveIcon : ourAuthoHideOffInactiveIcon));
myHideButton.setVisible(true);
}
else if (myInfo.isFloating()) {
myToggleFloatingModeButton.setIcon(active ? ourFixIcon : ourFixInactiveIcon);
myToggleFloatingModeButton.setIcon(ourFixIcon, ourFixInactiveIcon);
myToggleDockModeButton.setVisible(false);
myDockAutoHideSeparator.setVisible(false);
myToggleAutoHideModeButton.setVisible(true);
myAutoHideHideSeparator.setVisible(true);
myToggleAutoHideModeButton.setIcon(active
? myInfo.isAutoHide() ? ourAuthoHideOnIcon : ourAuthoHideOffIcon
: myInfo.isAutoHide() ? ourAuthoHideOnInactiveIcon : ourAuthoHideOffInactiveIcon);
myToggleAutoHideModeButton.setIcon(myInfo.isAutoHide() ? ourAuthoHideOnIcon : ourAuthoHideOffIcon,
myInfo.isAutoHide() ? ourAuthoHideOnInactiveIcon : ourAuthoHideOffInactiveIcon);
myHideButton.setVisible(true);
}
else if (myInfo.isSliding()) {
myToggleFloatingModeButton.setIcon(active ? ourFloatingIcon : ourFloatingInactiveIcon);
myToggleFloatingModeButton.setIcon(ourFloatingIcon, ourFloatingInactiveIcon);
myToggleDockModeButton.setVisible(true);
myDockAutoHideSeparator.setVisible(true);
myToggleDockModeButton.setIcon(active ? ourDockedIcon : ourDockedInactiveIcon);
myToggleDockModeButton.setIcon(ourDockedIcon, ourDockedInactiveIcon);
myToggleAutoHideModeButton.setVisible(false);
myAutoHideHideSeparator.setVisible(false);
myHideButton.setVisible(true);
}
myHideButton.setIcon(active ? ourHideIcon : ourHideInactiveIcon);
myHideButton.setIcon(ourHideIcon, ourHideInactiveIcon);
//
updateTitle();
@@ -396,23 +386,13 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
// Compose title bar
myTitlePanel.addTitle(myTitleTabs);
final JPanel buttonPanel = new JPanel(new GridBagLayout());
final JPanel buttonPanel = new JPanel(new GridLayout(1, 4, 2, 0));
buttonPanel.setOpaque(false);
buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
buttonPanel.add(myToggleFloatingModeButton, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myFloatingDockSeparator, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myToggleDockModeButton, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myDockAutoHideSeparator, new GridBagConstraints(3, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myToggleAutoHideModeButton, new GridBagConstraints(4, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myAutoHideHideSeparator, new GridBagConstraints(5, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myHideButton, new GridBagConstraints(6, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
buttonPanel.add(myToggleFloatingModeButton);
buttonPanel.add(myToggleDockModeButton);
buttonPanel.add(myToggleAutoHideModeButton);
buttonPanel.add(myHideButton);
myTitlePanel.addButtons(buttonPanel, myHideSideButton);
@@ -926,15 +906,20 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
}
private static final class MyTitleButton extends Wrapper implements ActionListener {
private class MyTitleButton extends Wrapper implements ActionListener {
private final InplaceButton myButton;
private final AnAction myAction;
public MyTitleButton(AnAction action) {
myAction = action;
myButton = new InplaceButton(null, new EmptyIcon(16), this);
//myButton.setTransform(0, -1);
myButton = new InplaceButton(null, new EmptyIcon(16), this) {
@Override
public boolean isActive() {
return MyTitleButton.this.isActive();
}
};
myButton.setHoveringEnabled(false);
setContent(myButton);
setOpaque(false);
}
@@ -952,8 +937,12 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
myAction.actionPerformed(event);
}
public void setIcon(final Icon icon) {
myButton.setIcon(icon);
public boolean isActive() {
return InternalDecorator.this.computeActive();
}
public void setIcon(final Icon active, Icon inactive) {
myButton.setIcons(active, inactive, active);
}
@@ -1010,20 +999,6 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa
// }
//}
private static final class Separator extends JLabel {
private static final Icon ourActiveSeparator = IconLoader.getIcon("/general/separator.png");
private static final Icon ourInactiveSeparator = IconLoader.getIcon("/general/inactiveSeparator.png");
public final void setActive(final boolean active) {
setIcon(active ? ourActiveSeparator : ourInactiveSeparator);
}
public final void updateUI() {
super.updateUI();
setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
}
}
/**
* Synchronizes decorator with IdeToolWindow changes.
*/
@@ -28,7 +28,7 @@ import java.awt.*;
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
public final class TitlePanel extends JPanel {
public class TitlePanel extends JPanel {
private static final Color CNT_COLOR = new SameColor(184);
private static final Color BND_COLOR = CNT_COLOR;
@@ -80,8 +80,10 @@ public final class TitlePanel extends JPanel {
final Graphics2D g2d = (Graphics2D) g;
Color bndColor = myActive ? BND_ACTIVE_COLOR : BND_COLOR;
Color cntColor = myActive ? CNT_ACTIVE_COLOR : CNT_COLOR;
boolean active = isActive();
Color bndColor = active ? BND_ACTIVE_COLOR : BND_COLOR;
Color cntColor = active ? CNT_ACTIVE_COLOR : CNT_COLOR;
g2d.setPaint(new GradientPaint(0, STRUT, bndColor, 0, getHeight(), cntColor));
if (mySideButtons.isVisible()) {
@@ -89,12 +91,12 @@ public final class TitlePanel extends JPanel {
g2d.fillRect(0, STRUT, getWidth() - sideRec.width, getHeight());
g2d.setColor(UIUtil.getHeaderInactiveColor());
final Color buttonInnerColor = myActive ? ACTIVE_SIDE_BUTTON_BG : INACTIVE_SIDE_BUTTON_BG;
final Color buttonInnerColor = active ? ACTIVE_SIDE_BUTTON_BG : INACTIVE_SIDE_BUTTON_BG;
g2d.setPaint(new GradientPaint(sideRec.x, sideRec.y, Color.white, sideRec.x, (int)sideRec.getMaxY() - 1, buttonInnerColor));
g2d.fillRect(sideRec.x + 2, sideRec.y, sideRec.width - 2, sideRec.height);
Icon separator = myActive ? mySeparatorActive : mySeparatorInactive;
Icon separator = active ? mySeparatorActive : mySeparatorInactive;
separator.paintIcon(this, g, sideRec.x, sideRec.y);
} else {
@@ -121,4 +123,8 @@ public final class TitlePanel extends JPanel {
add(wrapper, BorderLayout.EAST);
}
public boolean isActive() {
return myActive;
}
}
@@ -96,7 +96,9 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener;
private final InternalDecoratorListener myInternalDecoratorListener;
private boolean myEditorComponentActive;
private boolean myEditorWasActive;
private Boolean myEditorActive;
private final ActiveStack myActiveStack;
private final SideStack mySideStack;
@@ -127,11 +129,20 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}
}
};
private PropertyChangeListener myFocusListener;
private enum KeyState {
waiting, pressed, released, hold
}
private Alarm myUpdateHeadersAlarm = new Alarm();
private Runnable myUpdateHeadersRunnable = new Runnable() {
@Override
public void run() {
updateToolWindowHeaders();
}
};
/**
* invoked by reflection
*/
@@ -175,7 +186,6 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
myToolWindowPropertyChangeListener = new MyToolWindowPropertyChangeListener();
myInternalDecoratorListener = new MyInternalDecoratorListener();
myEditorComponentActive = false;
myActiveStack = new ActiveStack();
mySideStack = new SideStack();
@@ -196,6 +206,39 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
public void selectionChanged(FileEditorManagerEvent event) {
}
});
myFocusListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
myEditorActive = null;
if ("focusOwner".equals(evt.getPropertyName())) {
myUpdateHeadersAlarm.cancelAllRequests();
myUpdateHeadersAlarm.addRequest(myUpdateHeadersRunnable, 50);
}
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(myFocusListener);
}
private void updateToolWindowHeaders() {
getFocusManager().doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
WindowInfoImpl[] infos = myLayout.getInfos();
for (WindowInfoImpl each : infos) {
if (each.isVisible()) {
ToolWindow tw = getToolWindow(each.getId());
if (tw instanceof ToolWindowImpl) {
InternalDecorator decorator = ((ToolWindowImpl)tw).getDecorator();
if (decorator != null) {
decorator.repaint();
}
}
}
}
}
});
}
public boolean dispatchKeyEvent(KeyEvent e) {
@@ -314,6 +357,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}
public void disposeComponent() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(myFocusListener);
}
public void projectOpened() {
@@ -346,7 +390,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
myEditorComponentFocusWatcher.install(editorComponent);
appendSetEditorComponentCmd(editorComponent, commandsList);
if (myEditorComponentActive) {
if (myEditorWasActive) {
activateEditorComponentImpl(editorManager.getSplitters(), commandsList, true);
}
execute(commandsList);
@@ -537,7 +581,6 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}
deactivateWindows(postExecute, null);
myActiveStack.clear();
myEditorComponentActive = true;
execute(postExecute);
}
@@ -617,7 +660,6 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
if (toApplyInfo) {
appendApplyWindowInfoCmd(info, commandsList);
myActiveStack.push(id);
myEditorComponentActive = false;
}
if (autoFocusContents) {
@@ -1172,7 +1214,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
}
// if there is no any active tool window and editor is also inactive
// then activate editor
if (!myEditorComponentActive && getActiveToolWindowId() == null) {
if (!myEditorWasActive && getActiveToolWindowId() == null) {
activateEditorComponentImpl(getSplittersFromFocus(), commandList, true);
}
execute(commandList);
@@ -1308,7 +1350,14 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
public boolean isEditorComponentActive() {
ApplicationManager.getApplication().assertIsDispatchThread();
return myEditorComponentActive;
if (myEditorActive == null) {
Component owner = getFocusManager().getFocusOwner();
EditorsSplitters splitters = UIUtil.getParentOfType(EditorsSplitters.class, owner);
myEditorActive = splitters != null;
}
return myEditorActive;
}
ToolWindowAnchor getToolWindowAnchor(final String id) {
@@ -1669,7 +1718,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
for (final Object o : element.getChildren()) {
final Element e = (Element)o;
if (EDITOR_ELEMENT.equals(e.getName())) {
myEditorComponentActive = Boolean.valueOf(e.getAttributeValue(ACTIVE_ATTR_VALUE)).booleanValue();
myEditorWasActive = Boolean.valueOf(e.getAttributeValue(ACTIVE_ATTR_VALUE)).booleanValue();
}
else if (DesktopLayout.TAG.equals(e.getName())) { // read layout of tool windows
myLayout.readExternal(e);
@@ -1709,7 +1758,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(myFrame.getExtendedState()));
// Save whether editor is active or not
final Element editorElement = new Element(EDITOR_ELEMENT);
editorElement.setAttribute(ACTIVE_ATTR_VALUE, myEditorComponentActive ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
editorElement.setAttribute(ACTIVE_ATTR_VALUE, isEditorComponentActive() ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
element.addContent(editorElement);
// Save layout of tool windows
final Element layoutElement = new Element(DesktopLayout.TAG);
@@ -1836,7 +1885,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
// Sometimes focus gained comes when editor is active. For example it can happen when
// user switches between menus or closes some dialog. In that case we just ignore this event,
// i.e. don't initiate deactivation of tool windows and requesting focus in editor.
if (myEditorComponentActive) {
if (isEditorComponentActive()) {
return;
}
@@ -2044,7 +2093,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
private ActionCallback processDefaultFocusRequest(boolean forced) {
if (ModalityState.NON_MODAL.equals(ModalityState.current())) {
final String activeId = getActiveToolWindowId();
if (myEditorComponentActive || activeId == null || getToolWindow(activeId) == null) {
if (isEditorComponentActive() || activeId == null || getToolWindow(activeId) == null) {
activateEditorComponent(forced, true);
}
else {