mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[ui] fixes app activation/deactivation tracking via window focus events (IDEA-142728)
This commit is contained in:
+1
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -34,24 +34,11 @@ public interface ApplicationActivationListener {
|
||||
*/
|
||||
void applicationDeactivated(IdeFrame ideFrame);
|
||||
|
||||
/**
|
||||
* This is more precise notification than {code applicationDeactivated} callback.
|
||||
* It is intended for focus subsystem and purposes where we do not want
|
||||
* to be bothered by false application deactivation events.
|
||||
*
|
||||
* The shortcoming of the method is that a notification is delivered
|
||||
* with a delay. See {code app.deactivation.timeout} key in the registry
|
||||
*/
|
||||
void delayedApplicationDeactivated(IdeFrame ideFrame);
|
||||
|
||||
abstract class Adapter implements ApplicationActivationListener {
|
||||
@Override
|
||||
public void applicationActivated(IdeFrame ideFrame) { }
|
||||
|
||||
@Override
|
||||
public void applicationDeactivated(IdeFrame ideFrame) { }
|
||||
|
||||
@Override
|
||||
public void delayedApplicationDeactivated(IdeFrame ideFrame) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,7 @@ import com.intellij.openapi.util.ExpirableRunnable;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.ex.WindowManagerEx;
|
||||
import com.intellij.openapi.wm.impl.FocusManagerImpl;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -65,7 +62,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author Vladimir Kondratyev
|
||||
@@ -540,7 +536,9 @@ public class IdeEventQueue extends EventQueue {
|
||||
|
||||
myEventCount++;
|
||||
|
||||
if (processAppActivationEvents(e)) return;
|
||||
if (e instanceof WindowEvent) {
|
||||
processAppActivationEvents((WindowEvent)e);
|
||||
}
|
||||
|
||||
if (!typeAheadFlushing) {
|
||||
fixStickyFocusedComponents(e);
|
||||
@@ -794,72 +792,35 @@ public class IdeEventQueue extends EventQueue {
|
||||
return peekEvent(WindowEvent.WINDOW_OPENED) != null;
|
||||
}
|
||||
|
||||
private static AtomicLong requestToDeactivateTime = new AtomicLong(System.currentTimeMillis());
|
||||
private static final int APP_DEACTIVATION_DELAY = 50;
|
||||
private static final Alarm ourDeactivationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
|
||||
|
||||
private static boolean processAppActivationEvents(AWTEvent e) {
|
||||
Application app = ApplicationManager.getApplication();
|
||||
if (!(app instanceof ApplicationImpl)) return false;
|
||||
final ApplicationImpl appImpl = (ApplicationImpl)app;
|
||||
|
||||
if (e instanceof WindowEvent) {
|
||||
final WindowEvent we = (WindowEvent)e;
|
||||
|
||||
// Only Dialogs and Frames get window activated events.
|
||||
// Let's handle Windows this way.
|
||||
|
||||
final Window eventWindow = we.getWindow();
|
||||
if (we.getID() == WindowEvent.WINDOW_ACTIVATED || we.getID() == WindowEvent.WINDOW_GAINED_FOCUS) {
|
||||
//if () {
|
||||
// getPopupManager().closeAllPopups();
|
||||
//}
|
||||
appImpl.myCancelDeactivation = true;
|
||||
if (!appImpl.isActive()) {
|
||||
appImpl.tryToApplyActivationState(eventWindow, true, false);
|
||||
/*
|
||||
* This method processes "external" focus events (i.e. those induced by a window manager) and tracks
|
||||
* an application activation/deactivation state from them (an application is active if at least one it's windows has the focus).
|
||||
*
|
||||
* One special case is closing a child dialog: it generates a focus-lost event immediately followed by a focus-gained one.
|
||||
* To avoid (or rather reduce a chance of) unneeded app-deactivation event the processing of the focus-lost event
|
||||
* is slightly delayed.
|
||||
*/
|
||||
private static void processAppActivationEvents(final WindowEvent e) {
|
||||
if ((e.getID() == WindowEvent.WINDOW_GAINED_FOCUS || e.getID() == WindowEvent.WINDOW_LOST_FOCUS) && e.getOppositeWindow() == null) {
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
if (app instanceof ApplicationImpl) {
|
||||
if (e.getID() == WindowEvent.WINDOW_GAINED_FOCUS) {
|
||||
ourDeactivationAlarm.cancelAllRequests();
|
||||
((ApplicationImpl)app).tryToApplyActivationState(e.getWindow(), true);
|
||||
}
|
||||
}
|
||||
else if (we.getID() == WindowEvent.WINDOW_DEACTIVATED) {
|
||||
requestToDeactivateTime.getAndSet(System.currentTimeMillis());
|
||||
|
||||
// For stuff that cannot wait we notify about deactivation immediately
|
||||
appImpl.tryToApplyActivationState(eventWindow, false, true);
|
||||
|
||||
// We do not know for sure that application is going to be inactive,
|
||||
// we could just be showing a popup or another transient window.
|
||||
// So let's postpone the application deactivation for a while
|
||||
appImpl.myCancelDeactivation = false;
|
||||
|
||||
Timer timer = new Timer(Registry.intValue("app.deactivation.timeout"), new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
if (appImpl.isActiveDelayed() && !appImpl.isDeactivationCanceled()) {
|
||||
appImpl.tryToApplyActivationState(eventWindow, false, false);
|
||||
else {
|
||||
ourDeactivationAlarm.addRequest(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((ApplicationImpl)app).tryToApplyActivationState(e.getWindow(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
}
|
||||
|
||||
if (we.getID() == WindowEvent.WINDOW_DEACTIVATED || we.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
|
||||
Component frame = UIUtil.findUltimateParent(eventWindow);
|
||||
Component focusOwnerInDeactivatedWindow = eventWindow.getMostRecentFocusOwner();
|
||||
IdeFrame[] allProjectFrames = WindowManager.getInstance().getAllProjectFrames();
|
||||
|
||||
if (focusOwnerInDeactivatedWindow != null) {
|
||||
for (IdeFrame ideFrame : allProjectFrames) {
|
||||
JFrame aFrame = WindowManager.getInstance().getFrame(ideFrame.getProject());
|
||||
if (aFrame.equals(frame)) {
|
||||
IdeFocusManager focusManager = IdeFocusManager.getGlobalInstance();
|
||||
if (focusManager instanceof FocusManagerImpl) {
|
||||
((FocusManagerImpl)focusManager).setLastFocusedAtDeactivation(ideFrame, focusOwnerInDeactivatedWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, APP_DEACTIVATION_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void defaultDispatchEvent(final AWTEvent e) {
|
||||
|
||||
+7
-44
@@ -129,16 +129,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
private static final String WAS_EVER_SHOWN = "was.ever.shown";
|
||||
|
||||
private volatile boolean myActive;
|
||||
private volatile boolean myActiveDelayed;
|
||||
public volatile boolean myCancelDeactivation;
|
||||
|
||||
private static final int IS_EDT_FLAG = 1<<30; // we don't mess with sign bit since we want to do arithmetic
|
||||
private static final int IS_READ_LOCK_ACQUIRED_FLAG = 1<<29;
|
||||
|
||||
public boolean isActiveDelayed() {
|
||||
return myActiveDelayed;
|
||||
}
|
||||
|
||||
private static class Status {
|
||||
// higher three bits are for IS_* flags
|
||||
// lower bits are for edtSafe counter
|
||||
@@ -1193,10 +1187,6 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDeactivationCanceled() {
|
||||
return myCancelDeactivation;
|
||||
}
|
||||
|
||||
/**
|
||||
* !!!!! CAUTION !!!!!
|
||||
* !!!!! CAUTION !!!!!
|
||||
@@ -1212,58 +1202,31 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
*
|
||||
* There are no legitimate usages of the method outside of IdeEventQueue.processAppActivationEvents()
|
||||
*/
|
||||
public boolean tryToApplyActivationState(Window window, boolean activation, boolean immediate) {
|
||||
return activation ? applyActivation(window) :
|
||||
immediate ? applyDeactivation(window)
|
||||
: applyDelayedDeactivation(window);
|
||||
}
|
||||
|
||||
private boolean applyActivation(Window window) {
|
||||
if (!isActive()) {
|
||||
public void tryToApplyActivationState(Window window, boolean activation) {
|
||||
if (activation && !isActive()) {
|
||||
myActive = true;
|
||||
myActiveDelayed = true;
|
||||
IdeFrame ideFrame = getIdeFrameFromWindow(window);
|
||||
if (ideFrame != null) {
|
||||
getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationActivated(ideFrame);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean applyDeactivation(Window window) {
|
||||
if (isActive()) {
|
||||
else if (!activation && isActive()) {
|
||||
myActive = false;
|
||||
IdeFrame ideFrame = getIdeFrameFromWindow(window);
|
||||
if (ideFrame != null) {
|
||||
getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationDeactivated(ideFrame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean applyDelayedDeactivation(Window window) {
|
||||
if (isActiveDelayed()) {
|
||||
myActiveDelayed = false;
|
||||
IdeFrame ideFrame = getIdeFrameFromWindow(window);
|
||||
if (ideFrame != null) {
|
||||
getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).delayedApplicationDeactivated(ideFrame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IdeFrame getIdeFrameFromWindow (Window window) {
|
||||
final Component frame = UIUtil.findUltimateParent(window);
|
||||
return (frame instanceof IdeFrame) ? (IdeFrame)frame : null;
|
||||
private static IdeFrame getIdeFrameFromWindow(Window window) {
|
||||
Component frame = UIUtil.findUltimateParent(window);
|
||||
return frame instanceof IdeFrame ? (IdeFrame)frame : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
if (isUnitTestMode()) return true;
|
||||
|
||||
return KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() != null || myActive;
|
||||
return isUnitTestMode() || myActive;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -1069,7 +1069,6 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable {
|
||||
}
|
||||
|
||||
private class AppListener extends ApplicationActivationListener.Adapter {
|
||||
|
||||
@Override
|
||||
public void applicationActivated(final IdeFrame ideFrame) {
|
||||
final FocusCommand cmd = myFocusCommandOnAppActivation;
|
||||
@@ -1085,13 +1084,13 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedApplicationDeactivated(IdeFrame ideFrame) {
|
||||
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
Component parent = UIUtil.findUltimateParent(owner);
|
||||
public void applicationDeactivated(IdeFrame ideFrame) {
|
||||
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
Component parent = UIUtil.findUltimateParent(owner);
|
||||
|
||||
if (parent == ideFrame) {
|
||||
myLastFocusedAtDeactivation.put(ideFrame, owner);
|
||||
}
|
||||
if (parent == ideFrame) {
|
||||
myLastFocusedAtDeactivation.put(ideFrame, owner);
|
||||
}
|
||||
}
|
||||
|
||||
private void focusLastFocusedComponent(IdeFrame ideFrame) {
|
||||
|
||||
@@ -621,7 +621,6 @@ tests.view.old.statistics.panel=false
|
||||
|
||||
tfs.set.connection.timeout=false
|
||||
|
||||
app.deactivation.timeout=1500
|
||||
ide.mac.yosemite.laf=false
|
||||
ide.mac.yosemite.laf.restartRequired=true
|
||||
ide.mac.yosemite.laf.description=Replaces Aqua LaF to enhanced IntelliJ LaF
|
||||
|
||||
Reference in New Issue
Block a user