Skip to content

Conversation

@trancexpress
Copy link
Contributor

When switching a perspective, if a view exists in both the old and the
new perspective, it doesn't gain focus after the perspective switch.
This is contrary to the case in which a view is open only in the new
perspective. Since editors are shared between perspectives, the same bug
always occurs for editors - the editor will lose focus on a perspective
switch.

This change adds part focusing code on the code branch, on which the
active part already exists in the new perspective. This ensures the
active part gains focus.

Note that this change doesn't preserve focus of the active part in each
perspective. I.e. if View A is active in perspective X and View B was
active in perspective Y, upon switching to perspective Y, View A will
become active if it exists. The change only ensures that focus is not
lost altogether.

Fixes: #2839

@trancexpress
Copy link
Contributor Author

I'm not sure if the problem is seen only on Linux... I don't have a Windows machine to test on. The change here might not be needed on Windows... Or Mac.

@trancexpress
Copy link
Contributor Author

trancexpress commented Mar 17, 2025

Note that this change doesn't preserve focus of the active part in each
perspective. I.e. if View A is active in perspective X and View B was
active in perspective Y, upon switching to perspective Y, View A will
become active if it exists. The change only ensures that focus is not
lost altogether.

Ideally the correct part receives focus... I.e. whatever was focused in perspective X will get focused again when switching to X... @iloveeclipse are we looking into that? The initial bug report complained about losing focus in Eclipse; I'm not sure what the user is expecting of the focus when switching back and forth between perspectives.

No idea how complicated keeping the correct active part for each perspective will be. Right now PartServiceImpl just looks for the active part of the current perspective in the new perspective. Then activates that in the new perspective, if found. If not found, then it looks for the previously active part (of the new perspective, I think)...

@eclipse-platform-bot
Copy link
Contributor

eclipse-platform-bot commented Mar 17, 2025

This pull request changes some projects for the first time in this development cycle.
Therefore the following files need a version increment:

bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF

An additional commit containing all the necessary changes was pushed to the top of this PR's branch. To obtain these changes (for example if you want to push more changes) either fetch from your fork or apply the git patch.

Git patch
From e30587921cff2d299f67938420006a3e81bbecdb Mon Sep 17 00:00:00 2001
From: Eclipse Platform Bot <[email protected]>
Date: Tue, 18 Mar 2025 07:48:06 +0000
Subject: [PATCH] Version bump(s) for 4.36 stream


diff --git a/bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF
index 5940a65841..73fb49dc0a 100644
--- a/bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.ui.workbench/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-SymbolicName: org.eclipse.e4.ui.workbench;singleton:=true
-Bundle-Version: 1.17.0.qualifier
+Bundle-Version: 1.17.100.qualifier
 Bundle-Name: %pluginName
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
-- 
2.48.1

Further information are available in Common Build Issues - Missing version increments.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2025

Test Results

 1 824 files  +  1   1 824 suites  +1   1h 36m 9s ⏱️ + 5m 34s
 7 918 tests ±  0   7 690 ✅ +  1  228 💤 ±0  0 ❌  - 1 
23 841 runs  +121  23 093 ✅ +122  748 💤 ±0  0 ❌  - 1 

Results for commit c2bd1ee. ± Comparison against base commit 7f9ec57.

♻️ This comment has been updated with latest results.

@iloveeclipse
Copy link
Member

Interesting one. Seem to be simple :-)

What I see is that in the cases where switch perspective happens we have now fixed the problem.

However, the new code

  1. forces focus twice in the fixed case
  2. forces focus on active view on every window activation now (if one switches between Eclipse and other windows)

I'm not sure regarding the second point if that is good or bad. I also didn't checked what would happen in multi-window Eclipse case.

You can see what is being focused by switching /trace/focus flag for org.eclipse.e4.ui.workbench.swt.

@trancexpress
Copy link
Contributor Author

I also didn't checked what would happen in multi-window Eclipse case.

I don't see any weird behavior when using multiple windows. Any specific scenario you want tested?

What I see is that in the cases where switch perspective happens we have now fixed the problem.

However, the new code

1. forces focus **twice** in the fixed case
2. forces focus on active view on every window activation now (if one switches between Eclipse and other windows)

I'm not sure regarding the second point if that is good or bad.
You can see what is being focused by switching /trace/focus flag for org.eclipse.e4.ui.workbench.swt.

Odd, the new code leads to recursive activation:

| main | 2025-03-18 08:13:56.543 | org.eclipse.e4.ui.workbench.swt | /trace/focus | org.eclipse.e4.ui.internal.workbench.swt.WorkbenchSWTActivator | trace | 125 | Focused GUI on element: org.eclipse.ui.console.ConsoleView=org.eclipse.e4.ui.model.application.ui.basic.impl.PartImpl@6d2a2560 (tags: [View, categoryTag:General, active], contributorURI: null) (widget: ContributedPartRenderer$1 {} [layout=org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer$2@1e429f56], renderer: org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer@75f8d9b0, toBeRendered: true, onTop: false, visible: true, containerData: null, accessibilityPhrase: null) (contributionURI: bundleclass://org.eclipse.ui.workbench/org.eclipse.ui.internal.e4.compatibility.CompatibilityView, object: CompatibilityPart [partId=org.eclipse.ui.console.ConsoleView, properties=[], tags=[View, categoryTag:General, active], wrapped=class org.eclipse.ui.internal.console.ConsoleView, legacyPart=class org.eclipse.ui.internal.console.ConsoleView, beingDisposed=false, alreadyDisposed=false], context: PartImpl (org.eclipse.ui.console.ConsoleView) Context, variables: [], label: Console, iconURI: platform:/plugin/org.eclipse.ui.console/icons/full/cview16/console_view.png, tooltip: , dirty: false, closeable: true, description: null) |
java.lang.Exception: FOCUSED GUI
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.focusGui(PartRenderingEngine.java:796)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.focusPart(PartServiceImpl.java:1538)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.activate(PartServiceImpl.java:747)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.activate(PartServiceImpl.java:699)
	at org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer.activate(AbstractPartRenderer.java:95)
	at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer.lambda$0(ContributedPartRenderer.java:63)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1678)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
	at org.eclipse.swt.widgets.Shell.setActiveControl(Shell.java:2231)
	at org.eclipse.swt.widgets.Shell.setActiveControl(Shell.java:2194)
	at org.eclipse.swt.widgets.Control.sendFocusEvent(Control.java:4898)
	at org.eclipse.swt.widgets.Control.gtk_event_after(Control.java:3750)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2620)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6842)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6169)
	at org.eclipse.swt.internal.gtk.GTK.gtk_widget_grab_focus(Native Method)
	at org.eclipse.swt.widgets.Control.forceFocus(Control.java:2905)
	at org.eclipse.swt.widgets.Composite.forceFocus(Composite.java:723)
	at org.eclipse.swt.widgets.Control.forceFocus(Control.java:2898)
	at org.eclipse.swt.widgets.Control.setFocus(Control.java:5475)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1690)
	at org.eclipse.swt.custom.StyledText.setFocus(StyledText.java:8759)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1688)
	at org.eclipse.ui.part.PageBookView.setFocus(PageBookView.java:826)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.delegateSetFocus(CompatibilityPart.java:226)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:56)
	at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:299)
	at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:305)
	at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:227)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:148)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.focusGui(PartRenderingEngine.java:789)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.focusPart(PartServiceImpl.java:1538)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.activate(PartServiceImpl.java:747)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.switchPerspectiveInternal(PartServiceImpl.java:643)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.switchPerspective(PartServiceImpl.java:613)
	at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.showTab(PerspectiveStackRenderer.java:99)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.lambda$0(LazyStackRenderer.java:84)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:133)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:5967)
	at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:168)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:187)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.lambda$0(PerspectiveSwitcher.java:524)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.widgetSelected(PerspectiveSwitcher.java:522)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:286)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5072)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4524)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1152)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1043)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:668)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:576)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:713)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:647)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1588)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1560)
| main | 2025-03-18 08:13:56.544 | org.eclipse.e4.ui.workbench.swt | /trace/focus | org.eclipse.e4.ui.internal.workbench.swt.WorkbenchSWTActivator | trace | 125 | Focused GUI on element: org.eclipse.ui.console.ConsoleView=org.eclipse.e4.ui.model.application.ui.basic.impl.PartImpl@6d2a2560 (tags: [View, categoryTag:General, active], contributorURI: null) (widget: ContributedPartRenderer$1 {} [layout=org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer$2@1e429f56], renderer: org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer@75f8d9b0, toBeRendered: true, onTop: false, visible: true, containerData: null, accessibilityPhrase: null) (contributionURI: bundleclass://org.eclipse.ui.workbench/org.eclipse.ui.internal.e4.compatibility.CompatibilityView, object: CompatibilityPart [partId=org.eclipse.ui.console.ConsoleView, properties=[], tags=[View, categoryTag:General, active], wrapped=class org.eclipse.ui.internal.console.ConsoleView, legacyPart=class org.eclipse.ui.internal.console.ConsoleView, beingDisposed=false, alreadyDisposed=false], context: PartImpl (org.eclipse.ui.console.ConsoleView) Context, variables: [], label: Console, iconURI: platform:/plugin/org.eclipse.ui.console/icons/full/cview16/console_view.png, tooltip: , dirty: false, closeable: true, description: null) |
java.lang.Exception: FOCUSED GUI
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.focusGui(PartRenderingEngine.java:796)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.focusPart(PartServiceImpl.java:1538)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.activate(PartServiceImpl.java:747)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.switchPerspectiveInternal(PartServiceImpl.java:643)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.switchPerspective(PartServiceImpl.java:613)
	at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.showTab(PerspectiveStackRenderer.java:99)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.lambda$0(LazyStackRenderer.java:84)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:133)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:5967)
	at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:168)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:187)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.lambda$0(PerspectiveSwitcher.java:524)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.widgetSelected(PerspectiveSwitcher.java:522)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:286)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5072)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4524)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1152)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1043)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:668)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:576)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:713)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:647)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1588)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1560)

I'll look into this.

When switching a perspective, if a view exists in both the old and the
new perspective, it doesn't gain focus after the perspective switch.
This is contrary to the case in which a view is open only in the new
perspective. Since editors are shared between perspectives, the same bug
always occurs for editors - the editor will lose focus on a perspective
switch.

This change adds part focusing code on the code branch, on which the
active part already exists in the new perspective. This ensures the
active part gains focus.

Note that this change doesn't preserve focus of the active part in each
perspective. I.e. if View A is active in perspective X and View B was
active in perspective Y, upon switching to perspective Y, View A will
become active if it exists. The change only ensures that focus is not
lost altogether.

Fixes: eclipse-platform#2839
Signed-off-by: Simeon Andreev <[email protected]>
UIEvents.publishEvent(UIEvents.UILifeCycle.ACTIVATE, part);
if (Policy.DEBUG_FOCUS) {
Activator.trace(Policy.DEBUG_FOCUS_FLAG, "Trying to activate already active part: " + part, null);//$NON-NLS-1$
if (!focusingActivePart) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting ugly... We can also take code from PartRenderingEngine.focusGui(MUIElement) to only try to force focus by calling AbstractPartRenderer.forceFocus(MUIElement)... if that works differently. But I'm not sure that works any better.

What about just leaving the double focusing in? Maybe it causes trouble in some client code which starts receiving 2 focus events instead of 0... With the flag for switching perspective protecting the focus call, I think that should be the only case of different behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let say it this way: most of e4 is ugly, but what should we do? At least this works.

@iloveeclipse
Copy link
Member

I don't see any weird behavior when using multiple windows. Any specific scenario you want tested?

Let assume debugging case, starting debugger from java perspective, have a breakpoint hit and see what happens if debug perspective should be activated. Which window / view will be activated, does it have focus, etc. Play around that.

@trancexpress
Copy link
Contributor Author

Let assume debugging case, starting debugger from java perspective, have a breakpoint hit and see what happens if debug perspective should be activated. Which window / view will be activated, does it have focus, etc. Play around that.

In the double-focus case (I assume if we merge anything, it will be similar to that), there is no difference in behavior. If I trigger debugging (with a breakpoint) from window 1 (in Java perspective), window 2 (in debug perspective) is activated on the breakpoint hit and the Debug view has the focus.

@laeubi
Copy link
Contributor

laeubi commented Mar 18, 2025

Of course views must handle the case to get focus more than once... but this usually means the view has lost focus before. As we don't know what custom views do on focus (e.g. the might itself send out an event, refresh some state, whatever) it seems wrong to trigger that event unnecessarily (e.g. if the view already has the focus).

@trancexpress
Copy link
Contributor Author

Of course views must handle the case to get focus more than once... but this usually means the view has lost focus before. As we don't know what custom views do on focus (e.g. the might itself send out an event, refresh some state, whatever) it seems wrong to trigger that event unnecessarily (e.g. if the view already has the focus).

I guess we can also debug why SWT/GTK+ is losing the focus in the first place, during the perspective switch. The platform UI code doesn't seem to expect this...

@iloveeclipse
Copy link
Member

I've played now with the patch and found out to my surprise that if one opens a second window, the original code without patch works as expected and focus is being set on the part after switching to the different perspective.

At same time same code doesn't work for the first window ?!? WHAT?

@trancexpress
Copy link
Contributor Author

trancexpress commented Mar 18, 2025

I've played now with the patch and found out to my surprise that if one opens a second window, the original code without patch works as expected and focus is being set on the part after switching to the different perspective.

At same time same code doesn't work for the first window ?!? WHAT?

The view with the missing focus must be open in both the old and new perspectives. See the reproduction steps on #2839. Have you ensured that?

When I do that in the 2nd window, I still see the bug.

@iloveeclipse
Copy link
Member

The view with the missing focus must be open in both the old and new perspectives.

Yes, that was it, I had to open second perspective in the second view and the selected views were different because of that.


private boolean constructed = false;

private boolean switchingPerspective = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't init to defaults, here and below, and to be consistent we can remove it from above too.

Copy link
Member

@iloveeclipse iloveeclipse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
I've checked on Windows, the patch seem to be not needed here (there are other oddities with the not proper active tab highlighting I don't see on Linux), but it seem not to harm either. I guess we are walking on a thin ice of CTabFolder & Co that are rendered / behave slightly different on different platforms.

UIEvents.publishEvent(UIEvents.UILifeCycle.ACTIVATE, part);
if (Policy.DEBUG_FOCUS) {
Activator.trace(Policy.DEBUG_FOCUS_FLAG, "Trying to activate already active part: " + part, null);//$NON-NLS-1$
if (!focusingActivePart) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let say it this way: most of e4 is ugly, but what should we do? At least this works.

@trancexpress
Copy link
Contributor Author

I guess we can also debug why SWT/GTK+ is losing the focus in the first place, during the perspective switch. The platform UI code doesn't seem to expect this...

Upon switching the perspective the first time, the focus is lost with this stack trace:

java.lang.Exception: Control focus out StyledText {} [layout=null]
	at org.eclipse.swt.widgets.Control.lambda$0(Control.java:796)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1678)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1657)
	at org.eclipse.swt.widgets.Control.sendFocusEvent(Control.java:4889)
	at org.eclipse.swt.widgets.Control.gtk_event_after(Control.java:3753)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2620)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6846)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6169)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_widget_reparent(Native Method)
	at org.eclipse.swt.widgets.Control.gtk_widget_reparent(Control.java:2857)
	at org.eclipse.swt.widgets.Control.setParent(Control.java:5874)
	at org.eclipse.e4.ui.workbench.renderers.swt.ElementReferenceRenderer.createWidget(ElementReferenceRenderer.java:76)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:991)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:658)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.processContents(LazyStackRenderer.java:205)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:672)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:72)
	at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:147)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:672)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:72)
	at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:147)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:672)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:72)
	at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveRenderer.processContents(PerspectiveRenderer.java:48)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:672)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:762)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:727)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:711)
	at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.showTab(PerspectiveStackRenderer.java:79)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.lambda$0(LazyStackRenderer.java:84)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:133)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:5967)
	at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:168)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:187)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.lambda$0(PerspectiveSwitcher.java:524)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.widgetSelected(PerspectiveSwitcher.java:522)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:286)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5072)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4524)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:668)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:576)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:713)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:647)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1588)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1560)

After that its not regained - this bug.

If I then manually focus the Console view and switch back to the original perspective (the 2nd perspective switch), the focus is again lost, but with this stack trace:

java.lang.Exception: Control focus out StyledText {} [layout=null]
	at org.eclipse.swt.widgets.Control.lambda$0(Control.java:796)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1678)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1657)
	at org.eclipse.swt.widgets.Control.sendFocusEvent(Control.java:4889)
	at org.eclipse.swt.widgets.Control.gtk_event_after(Control.java:3753)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:2620)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:6846)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:6169)
	at org.eclipse.swt.internal.gtk3.GTK3.gtk_widget_reparent(Native Method)
	at org.eclipse.swt.widgets.Control.gtk_widget_reparent(Control.java:2857)
	at org.eclipse.swt.widgets.Control.setParent(Control.java:5874)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showElementRecursive(LazyStackRenderer.java:303)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showElementRecursive(LazyStackRenderer.java:290)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showElementRecursive(LazyStackRenderer.java:367)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showElementRecursive(LazyStackRenderer.java:367)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showElementRecursive(LazyStackRenderer.java:367)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.showTab(LazyStackRenderer.java:226)
	at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.showTab(PerspectiveStackRenderer.java:85)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.lambda$0(LazyStackRenderer.java:84)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:133)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:5967)
	at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:168)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:187)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.lambda$0(PerspectiveSwitcher.java:524)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
	at org.eclipse.e4.ui.workbench.addons.perspectiveswitcher.PerspectiveSwitcher$3.widgetSelected(PerspectiveSwitcher.java:522)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:286)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5862)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1652)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:5072)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4524)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:668)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:576)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:178)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:713)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:647)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1588)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1560)

I'm not sure how we can avoid this... So far I've not been able to restore the lost focus in the SWT code.

@trancexpress
Copy link
Contributor Author

@iloveeclipse what about this change in SWT? eclipse-platform/eclipse.platform.swt#1916

Since its in SWT, maybe its a good idea to merge it early in the 4.36 cycle... to hopefully find any problems it might cause.

@trancexpress
Copy link
Contributor Author

Fixed with eclipse-platform/eclipse.platform.swt#1915 instead.

@laeubi
Copy link
Contributor

laeubi commented Mar 19, 2025

@trancexpress good job in identify and fix the root cause here! This will benefit other use-cases as well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eclipse loses focus after switching perspectives

4 participants