Bug 375576 - Controls are not fully realized when partOpened(...) , partBroughtToTop(...) and partActivated(...) are called
Summary: Controls are not fully realized when partOpened(...) , partBroughtToTop(...) ...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.1   Edit
Hardware: All All
: P3 major with 2 votes (vote)
Target Milestone: ---   Edit
Assignee: Patrik Suzzi CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
: 378842 383820 384112 385716 388653 389128 389453 389559 391351 415546 (view as bug list)
Depends on:
Blocks: 389478
  Show dependency tree
 
Reported: 2012-03-28 15:13 EDT by Lakshmi P Shanmugam CLA
Modified: 2021-05-18 08:11 EDT (History)
29 users (show)

See Also:


Attachments
screenshot (270.97 KB, image/tiff)
2012-03-28 15:13 EDT, Lakshmi P Shanmugam CLA
no flags Details
TextViewer workaround 2 (746 bytes, patch)
2012-08-20 14:47 EDT, Markus Keller CLA
no flags Details | Diff
Bug tested (case1) (79.82 KB, image/png)
2015-11-11 16:54 EST, Patrik Suzzi CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Lakshmi P Shanmugam CLA 2012-03-28 15:13:42 EDT
Created attachment 213307 [details]
screenshot

build id: 4.2 I-20120321-0610

Steps to reproduce:
1. Using Open Type dialog, open a file with long lines. One or more long lines should be somewhere in the beginning (first "page") of the file.
2. The file opens in the editor but it has been scrolled horizontally to the beginning of the Class name. 
The attached screenshot shows the problem.
Comment 1 Dani Megert CLA 2012-03-29 03:32:18 EDT
This works fine in 3.x. Looks like some broken/changed event flow when opening editors.
Comment 2 Dani Megert CLA 2012-03-29 03:34:24 EDT
Test case:

1. paste:
public class Bug {
// Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug
}

2. close the editor
3. use 'Open Type' to open 'Bug'
Comment 3 Dani Megert CLA 2012-03-29 05:14:06 EDT
This is caused by extensive requests to do a layout with SWT.DEFER (even on the shell). As a result, the part which is being opened, is not yet realized and hence all size and position computations are doomed to fail.

When executing this code:
takeAPartsControl.isLayoutDeferred();
in partOpened(...) or partActivated(...) 4.2 returns 'true', which is wrong.

This is a critical bug since it affects every part that wants to initialize itself based on its controls sizes and positions.
Comment 4 Dani Megert CLA 2012-03-29 08:31:11 EDT
Maybe this could be fixed within SWT by always forcing the layout when a client calls methods which rely on the layout. Tricky part with this is to distinguish calls from clients vs. internal ones.
Comment 5 Dani Megert CLA 2012-03-29 08:48:04 EDT
I tried to force a layout as a workaround but there doesn't seem to be a way  to force the layout once it's in deferred mode.
Comment 6 Bogdan Gheorghe CLA 2012-03-30 15:04:48 EDT
The only way to force a layout right now is by calling display.readAndDispatch(). 

You could call setLayoutDefered(false) while isLayoutDeffered() is true on the appropriate composite but I believe this will leave the deffered count unbalanced since next display.readAndDispatch() will also call setLayoutDeferred(false).

SWT could add API, another layout flag -> NOW which means the opposite of SWT.DEFER.  I am not sure that makes sense given that you still need to know how many times to call layout(..., SWT.NOW). Maybe SWT.NOW means call setlayoutDeferred(false) as many times as necessary.

But wouldn't this just defeat SWT.DEFER? What is the point.

Anyway, the problem only happens when there are no editors open. It should be possible to handle this above SWT.
Comment 7 Silenio Quarti CLA 2012-03-30 15:06:12 EDT
Comment above is mine.
Comment 8 Dani Megert CLA 2012-04-02 03:54:45 EDT
(In reply to comment #6)
> But wouldn't this just defeat SWT.DEFER? What is the point.

Right. I guess it's just overused in this case.
 

> Anyway, the problem only happens when there are no editors open.

No, it also happens when editors are open. Every part which gets opened and wants to do computations based on the layout is affected.
Comment 9 Eric Moffatt CLA 2012-04-04 09:12:49 EDT
OK, adding the following code (I put it in CompatibilityEditor at the end of the 'createPartControl' method):

   Display display = parent.getDisplay();
   while (display.readAndDispatch())
	;
   parent.layout(true, true);

While this appears to fix the issue by first flushing any DEFER'd layouts and then explicitly laying out the parent I'm concerned that spinning the event loop while opening *all* editors may introduce other regressions.

If there are no other cases then it would be better if this code was put into the editor itself rather than potentially affecting currently working editors.

Dani, what's your take ?
Comment 10 Dani Megert CLA 2012-04-04 09:15:20 EDT
(In reply to comment #9)
> OK, adding the following code (I put it in CompatibilityEditor at the end of
> the 'createPartControl' method):
> 
>    Display display = parent.getDisplay();
>    while (display.readAndDispatch())
>     ;
>    parent.layout(true, true);
> 
> While this appears to fix the issue by first flushing any DEFER'd layouts and
> then explicitly laying out the parent I'm concerned that spinning the event
> loop while opening *all* editors may introduce other regressions.
> 
> If there are no other cases then it would be better if this code was put into
> the editor itself rather than potentially affecting currently working editors.
> 
> Dani, what's your take ?

I suspect that driving the event loop might also have other side-effects. So, I'd neither add this for all parts nor for just the textual editors.
Comment 11 Eric Moffatt CLA 2012-04-04 09:38:36 EDT
Silenio just pointed out that we only need a *single* call to 'readAndDispatch()' in order to flush the DEFER'd layouts.

I've verified that this works and while I'm still a bit concerned about affecting other editors I'll take the initial hit.

To be clear, if this introduces *any* regressions in currently working editors I'll revert the change and the code will have to go into the editor itself.
Comment 12 Dani Megert CLA 2012-04-04 09:42:10 EDT
(In reply to comment #11)
> Silenio just pointed out that we only need a *single* call to
> 'readAndDispatch()' in order to flush the DEFER'd layouts.
> 
> I've verified that this works and while I'm still a bit concerned about
> affecting other editors I'll take the initial hit.
> 
> To be clear, if this introduces *any* regressions in currently working editors
> I'll revert the change and the code will have to go into the editor itself.

Note, that not only editors are affected, but also any parts that rely on the layout. The effect is incorrectly laid out parts. There might already be other bugs complaining about this.
Comment 13 Eric Moffatt CLA 2012-04-04 09:49:54 EDT
Dani, I know of no other instances where the current code pattern is causing similar issues, do you ? I'm not about to put this code (ok, hack) into more general use unless we can clearly identify that it's needed.

commit 26e36800be05a142d83b0dae6f37d885be5073d0

Pushed the changes with the single 'readAndDispatch()'...

I'll mark this as FIXED...Dani would you please get this and test whether you agree that it is indeed fixing the issue ?
Comment 14 Dani Megert CLA 2012-04-05 08:07:19 EDT
(In reply to comment #13)
> Dani, I know of no other instances where the current code pattern is causing
> similar issues, do you ?
No, but I guess that graphic editors like GEF might be affected.


> I'll mark this as FIXED...Dani would you please get this and test whether you
> agree that it is indeed fixing the issue ?

No, it is not, because after your fix is executed many other setLayoutDeferred(true) calls are made (e.g. when updating the action bars [1]). You need to make sure that the layout is done just before partOpened/Activated is sent.



[1]
Thread [main] (Suspended (breakpoint at line 1082 in Composite))	
	Shell(Composite).setLayoutDeferred(boolean) line: 1082	
	WorkbenchWindow.updateActionBars() line: 1795	
	WorkbenchWindow.updateActionSets() line: 1874	
	WorkbenchPage$ActionSwitcher.updateActionSets(ArrayList) line: 777	
	WorkbenchPage$ActionSwitcher.updateTopEditor(IEditorPart) line: 671	
	WorkbenchPage.updateActiveEditorSources(MPart) line: 353	
	WorkbenchPage.updateBroughtToTop(MPart) line: 397	
	WorkbenchPage.access$16(WorkbenchPage, MPart) line: 396	
	WorkbenchPage$E4PartListener.partBroughtToTop(MPart) line: 173	
	PartServiceImpl$6.run() line: 246	
	SafeRunner.run(ISafeRunnable) line: 42	
	PartServiceImpl.firePartBroughtToTop(MPart) line: 244	
	PartServiceImpl.access$4(PartServiceImpl, MPart) line: 242	
	PartServiceImpl$1.handleEvent(Event) line: 92	
	UIEventHandler$1.run() line: 41	
	UISynchronizer(Synchronizer).syncExec(Runnable) line: 180	
	UISynchronizer.syncExec(Runnable) line: 150	
	Display.syncExec(Runnable) line: 4683	
	E4Application$1.syncExec(Runnable) line: 184	
	UIEventHandler.handleEvent(Event) line: 38	
	EventHandlerWrapper.handleEvent(Event, Permission) line: 197	
	EventHandlerTracker.dispatchEvent(EventHandlerWrapper, Permission, int, Event) line: 197	
	EventHandlerTracker.dispatchEvent(Object, Object, int, Object) line: 1	
	EventManager.dispatchEvent(Set, EventDispatcher, int, Object) line: 230	
	ListenerQueue.dispatchEventSynchronous(int, Object) line: 148	
	EventAdminImpl.dispatchEvent(Event, boolean) line: 135	
	EventAdminImpl.sendEvent(Event) line: 78	
	EventComponent.sendEvent(Event) line: 39	
	EventBroker.send(String, Object) line: 81	
	UIEventPublisher.notifyChanged(Notification) line: 57	
	PartStackImpl(BasicNotifierImpl).eNotify(Notification) line: 374	
	PartStackImpl(ElementContainerImpl<T>).setSelectedElement(T) line: 171	
	ModelServiceImpl.showElementInWindow(MWindow, MUIElement) line: 418	
	ModelServiceImpl.bringToTop(MUIElement) line: 385	
	PartServiceImpl.delegateBringToTop(MPart) line: 578	
	PartServiceImpl.bringToTop(MPart) line: 314	
	PartServiceImpl.showPart(MPart, EPartService$PartState) line: 962	
	WorkbenchPage.busyOpenEditor(IEditorInput, String, boolean, int, IMemento, boolean) line: 3066	
	WorkbenchPage.access$22(WorkbenchPage, IEditorInput, String, boolean, int, IMemento, boolean) line: 2991	
	WorkbenchPage$8.run() line: 2973	
	BusyIndicator.showWhile(Display, Runnable) line: 70	
	WorkbenchPage.openEditor(IEditorInput, String, boolean, int, IMemento, boolean) line: 2969	
	WorkbenchPage.openEditor(IEditorInput, String, boolean, int) line: 2928	
	WorkbenchPage.openEditor(IEditorInput, String, boolean) line: 2919	
	EditorUtility.openInEditor(IEditorInput, String, boolean) line: 373	
	EditorUtility.openInEditor(Object, boolean) line: 179	
	OpenAction.run(Object[]) line: 249	
	OpenAction.run(IStructuredSelection) line: 228	
	OpenAction(SelectionDispatchAction).dispatchRun(ISelection) line: 275	
	OpenAction(SelectionDispatchAction).run() line: 251	
	PackageExplorerActionGroup.handleOpen(ISelection, boolean) line: 376	
	PackageExplorerPart$4.open(ISelection, boolean) line: 538	
	OpenAndLinkWithEditorHelper$InternalListener.open(OpenEvent) line: 48	
	StructuredViewer$2.run() line: 866	
	SafeRunner.run(ISafeRunnable) line: 42	
	JFaceUtil$1.run(ISafeRunnable) line: 49	
	SafeRunnable.run(ISafeRunnable) line: 175	
	PackageExplorerPart$PackageExplorerProblemTreeViewer(StructuredViewer).fireOpen(OpenEvent) line: 864	
	PackageExplorerPart$PackageExplorerProblemTreeViewer(StructuredViewer).handleOpen(SelectionEvent) line: 1152	
	StructuredViewer$6.handleOpen(SelectionEvent) line: 1256	
	OpenStrategy.fireOpenEvent(SelectionEvent) line: 275	
	OpenStrategy.access$2(OpenStrategy, SelectionEvent) line: 269	
	OpenStrategy$1.handleEvent(Event) line: 309	
	EventTable.sendEvent(Event) line: 84	
	Tree(Widget).sendEvent(Event) line: 1053	
	Display.runDeferredEvents() line: 4165	
	Display.readAndDispatch() line: 3754	
	PartRenderingEngine$9.run() line: 1016	
	Realm.runWithDefault(Realm, Runnable) line: 332	
	PartRenderingEngine.run(MApplicationElement, IEclipseContext) line: 910	
	E4Workbench.createAndRunUI(MApplicationElement) line: 85	
	Workbench$4.run() line: 580	
	Realm.runWithDefault(Realm, Runnable) line: 332	
	Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 535	
	PlatformUI.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 149	
	IDEApplication.start(IApplicationContext) line: 124	
	EclipseAppHandle.run(Object) line: 196	
	EclipseAppLauncher.runApplication(Object) line: 110	
	EclipseAppLauncher.start(Object) line: 79	
	EclipseStarter.run(Object) line: 353	
	EclipseStarter.run(String[], Runnable) line: 180	
	NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
	NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57	
	DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
	Method.invoke(Object, Object...) line: 601	
	Main.invokeFramework(String[], URL[]) line: 629	
	Main.basicRun(String[]) line: 584	
	Main.run(String[]) line: 1438	
	Main.main(String[]) line: 1414
Comment 15 Eric Moffatt CLA 2012-04-10 15:34:16 EDT
Dani, not sure I understand...can you give me a repro scenario that ends up wrong with the new code ?
Comment 16 Dani Megert CLA 2012-04-11 02:49:58 EDT
(In reply to comment #15)
> Dani, not sure I understand...can you give me a repro scenario that ends up
> wrong with the new code ?

The problem is that it sometimes indeed works but sometimes not (depending on whether other calls to setLayoutDeferred(true) happen before calling partOpened/Activated (seec comment 14).


I can easily reproduce (master and I20120410-0633) on Windows 7 with these steps:

0. start new workspace
1. close Welcome
2. paste this into Package Explorer:
public class Bug {
// Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug
}
3. Window > New Window
4. Open Type 'Bug'
==> now the bug might appear. If not, repeat step 3 and 4 until it appears. For me this normally happens in less than 3 attempts.
Comment 17 Eric Moffatt CLA 2012-04-11 12:29:15 EDT
Thanks Dani, I now see that the current 'fix' won't cut it...
Comment 18 Eric Moffatt CLA 2012-04-16 15:00:39 EDT
Dani, if you move the same 'fix' that I made in the CompatibilityEditor just before firing the event can you verify that this will fix the issue ?

I had some trouble finding this API, is it the one from PartService ?
Comment 19 Dani Megert CLA 2012-04-17 04:41:18 EDT
Adding the fix just at one place won't do, because lots of setLayoutDeferred(true) calls happen between partOpened, partBroughtToTop and partActivated. Therefore, we either have to fix the underlying issue of calling setLayoutDeferred(true) so many times, or we need to patch 3 places. I guess at this point, the latter is the way to go.

I've removed the changes made to CompatibilityEditor and added a fix to WorkbenchPage. The fix is less intrusive than the first attempt since I only drive the event loop once and don't force a layout. This is enough to flush the deferred layouts.

Fixed in master: http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=8d91da76608af4522ddd141b43c059e2448078de

Eric, please review the fix. Thanks.
Comment 20 Eric Moffatt CLA 2012-04-26 16:02:03 EDT
Dani, I'm seeing a strange NPE now....I'm running N20120420-2153 and don't see this in there but I do when ever I run an inner (debug) session when clicking on any view that has not been instantiated (i.e. gets lazily rendered).

java.lang.NullPointerException
	at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer$8.mouseUp(StackRenderer.java:878)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:220)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4169)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3758)
	at org.eclipse.ui.internal.WorkbenchPage.firePartOpened(WorkbenchPage.java:4365)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart$2.handleEvent(CompatibilityPart.java:110)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler$1.run(UIEventHandler.java:41)
Comment 21 Dani Megert CLA 2012-04-27 02:27:44 EDT
(In reply to comment #20)
> Dani, I'm seeing a strange NPE now....I'm running N20120420-2153 and don't see
> this in there but I do when ever I run an inner (debug) session when clicking
> on any view that has not been instantiated (i.e. gets lazily rendered).
> 
> java.lang.NullPointerException
>     at
> 

Did you revert my change to verify it's form that change? If so, please reopen this bug.
Comment 22 Brian de Alwis CLA 2012-04-27 08:04:11 EDT
I put a fix in for the NPE in bug 377837.  Eric, can you verify whether that fixes your NPEs too? (Though maybe it's papering over a different issue; everything seemed to work though.)
Comment 23 Eric Moffatt CLA 2012-04-27 09:10:03 EDT
I haven't reverted it yet but I do think I have a possible fix...

If you look at the trace where firePartOpened is called it's currently sent when the 'object' for the part goes non-null. This should likely just be moved to when the 'widget' is going non-null (the code for firing partClosed is already there...
Comment 24 Eric Moffatt CLA 2012-04-27 09:26:04 EDT
This defect scares me...here's the analysis:

1) we add a model check on 'object' and firePartOpened
2) in that code we ultimately spin the event loop
3) the 'mouseUp' happens to be the top event on the queue
4) the StackRenderer's 'mouseUp' handler fires and NPE's

To be that sensitive to the timing of a single event is an indication that our life cycle handling is not very robust.

Adding the NPE check is a good idea but in 4.3 we should take a closer look at this...

I've added Bug 377906 to track this...
Comment 25 Eric Moffatt CLA 2012-04-27 09:46:40 EDT
OK, the preliminary testing shows that moving the 'firePartOpened' up to when the 'widget' is set seems to solve this issue.

Now I have to test whether bug 337488 has regressed...other than that it looks OK. I'm going to get Bogdan to help me go over Dani's code just to be sure...
Comment 26 Eric Moffatt CLA 2012-04-27 11:05:01 EDT
OK...

a) The change to firing off 'partOpened' to when the widget goes non-null did not introduce a regression.

b) After checking it appears that the only part of Dani's commit that is needed to fix this issue is the code that was added to 'firePartActivated' (makes sense as this is the last event to fire so there are no opportunities for more deferred layouts to happen).

As per comment #24 we have to avoid this hack whenever possible so my preference is to revert the changes in the 'firePartOpened' and 'fireBroughtToTop' methods. Since we have no idea *what* event is on the queue any regressions (like the one we found) could be completely random and thus hard to fix. 

If we find that similar defects start appearing we can revisit this but for now I have to lean towards the least change possible.

Dani, I'd still prefer to remove this altogether...now that I've changed where 'partOpened' gets called to after the widget is created could you re-check whether you can add this code in your 'partActivated' implementation instead of in WorkbenchPage ? That would be the minimal use of the 'readAndDispatch' hack.
Comment 27 Eric Moffatt CLA 2012-04-27 15:37:42 EDT
commit 740d35da1cb0feda4ecc9bda7d855a8045886e19

This commit moves 'firePartOpened' to when the part's 'widget' goes non-null (as opposed tot eh 'object' as it was). This looks symmetrical now since we fire part closed when the widget goes null.

It also reverts some of Dani's original commit, leaving the only place we do the 'readAndDispatch' hack is in the firePartActivated method. This is about the least intrusive I can figure out how to make it (for now).

Dani, I've run the scenario multiple times but a second check would be useful...

Marking FIXED (again)
Comment 28 Dani Megert CLA 2012-04-30 09:14:20 EDT
(In reply to comment #27)
> commit 740d35da1cb0feda4ecc9bda7d855a8045886e19
> 
> This commit moves 'firePartOpened' to when the part's 'widget' goes non-null
> (as opposed tot eh 'object' as it was). This looks symmetrical now since we
> fire part closed when the widget goes null.
> 
> It also reverts some of Dani's original commit, leaving the only place we do
> the 'readAndDispatch' hack is in the firePartActivated method. This is about
> the least intrusive I can figure out how to make it (for now).
> 
> Dani, I've run the scenario multiple times but a second check would be
> useful...
> 
> Marking FIXED (again)

Sorry, but that change made things *way* worse, e.g. partOpened(...) is now no longer fired at all. I reverted your commit to fix this and to fix this original bug (again ;-).


If you make further changes please test them the following way before committing:

1. open the source for ResourceNavigator.java
2. add the following code:
	if (viewer.getTree().isLayoutDeferred())
		System.out.println("bug!");
	else
		System.out.println("works!);
to each part listener method:
    partListener.new IPartListener() {...}.partActivated(IWorkbenchPart)
    partListener.new IPartListener() {...}.partBroughtToTop(IWorkbenchPart)
    partListener.new IPartListener() {...}.partClosed(IWorkbenchPart)
    partListener.new IPartListener() {...}.partDeactivated(IWorkbenchPart)
    partListener.new IPartListener() {...}.partOpened(IWorkbenchPart)

3. add a breakpoint on the line with the "if" in each listener method
4. start the target
5. verify that each method is triggered when you open, activate, deactivate,
   bring-to-top and close views, and that the code goes through the "works!"
   code path

Thanks.
Comment 29 Eric Moffatt CLA 2012-04-30 10:12:36 EDT
Sheesh Dani, sorry...;-( You are correct that I completely broke partOpened...back to the drawing board <sigh>

To be clear my goal here (for M7) is to get the original test case (Bug Bug Bug Bug ...) to work correctly, not to match the old behavior exactly. However I'll see how close I can get.

BTW, does the code that I committed fix the 'bug bug' test case ?
Comment 30 Dani Megert CLA 2012-04-30 10:23:31 EDT
(In reply to comment #29)
> Sheesh Dani, sorry...;-( You are correct that I completely broke
> partOpened...back to the drawing board <sigh>
> 
> To be clear my goal here (for M7) is to get the original test case (Bug Bug Bug
> Bug ...) to work correctly, not to match the old behavior exactly.

We not only have to make partActivated work but also partOpened. That's what my fix did and now does again.


> However I'll
> see how close I can get.
> 
> BTW, does the code that I committed fix the 'bug bug' test case ?

I didn't bother spending further time on this, given the breakage it caused.
Comment 31 Eric Moffatt CLA 2012-04-30 14:06:52 EDT
OK, I'll leave this code in even with my concerns as outlined in comment #24...

Should we start seeing issues we can come back then, otherwise we'll fix this properly in 4.3.

Note that the partOpened event is still generated when the control is null, if this is going to be an issue please let me know right away...if we hadn't added the null check the StackRenderer would still be throwing NPE's...
Comment 32 Eric Moffatt CLA 2012-05-07 11:21:12 EDT
Verified in I20120503-1800.
Comment 33 Dani Megert CLA 2012-05-08 10:35:33 EDT
*** Bug 378842 has been marked as a duplicate of this bug. ***
Comment 34 Eric Moffatt CLA 2012-05-30 11:31:00 EDT
We've had to revert the part of this work that was calling 'readAdnDispatch' because it was causing serious reversions in the CTabFolder handling on the Mac.

I'll re-open this for another look during 4.2.1.
Comment 35 Dani Megert CLA 2012-05-30 11:33:12 EDT
(In reply to comment #34)
> We've had to revert the part of this work that was calling 'readAdnDispatch'
> because it was causing serious reversions in the CTabFolder handling on the
> Mac.

What's the bug number?
Comment 36 Paul Webster CLA 2012-05-30 11:34:26 EDT
Bug 379128

PW
Comment 37 Dani Megert CLA 2012-05-31 09:32:29 EDT
The real fix should really get rid of all the deferred layout requests.
Comment 38 Dani Megert CLA 2012-06-29 02:39:21 EDT
*** Bug 383820 has been marked as a duplicate of this bug. ***
Comment 39 Gary Karasiuk CLA 2012-06-29 07:35:07 EDT
(In reply to comment #38)
> *** Bug 383820 has been marked as a duplicate of this bug. ***

I see that this bug is marked as Critical, which is good, but I want to reinforce how serious this bug is for Mac users. It really compromises our ability to jump around in the Java IDE through following references.
Comment 40 Dani Megert CLA 2012-07-03 04:56:37 EDT
*** Bug 384112 has been marked as a duplicate of this bug. ***
Comment 41 Dani Megert CLA 2012-07-03 09:47:24 EDT
*** Bug 384156 has been marked as a duplicate of this bug. ***
Comment 42 Markus Keller CLA 2012-08-08 19:47:34 EDT
*** Bug 385716 has been marked as a duplicate of this bug. ***
Comment 43 Eric Moffatt CLA 2012-08-13 16:06:48 EDT
Dani, there may be a quick fix for this on your side...before you do your layout call 'setLayoutDeferred(false)' on the shell. I'm not saying that we shouldn't then work with SWT to better understand the interactions between the two modes going forward but this is likely the quickest path to a working solution...
Comment 44 Eric Moffatt CLA 2012-08-13 16:08:44 EDT
Hmm, just re-read the whole defect and it seems this may not work but it may still be worth a shot if we actually haven't attempted it yet.
Comment 45 Dani Megert CLA 2012-08-14 03:17:51 EDT
(In reply to comment #44)
> Hmm, just re-read the whole defect and it seems this may not work but it may
> still be worth a shot if we actually haven't attempted it yet.

There are several issues with that suggestion:
1. One setDeferredLayout(false) call won't work because the framework 
   excessively makes deferred=true calls. Clients would have to call the method
   until the layout isn't deferred anymore.

2. The client has many places where it expects the stuff to be layouted and 
   asks for some values, e.g. StyledText.getHorizontalPixel(). We can't go
   and find/hack all those places to workaround a severe framework bug.

3. All other editors would still suffer from the problem.
Comment 46 Eric Moffatt CLA 2012-08-15 15:00:07 EDT
Dani, I think that you are right in that the correct solution to this will have to come from SWT. It looks like a non-deferred layout should first run the deferred layouts.

However, I don't think that this will happen in the service releases (certainly not SR1 and likely not SR2). Silenio, correct me if I'm wrong here...

The platform may indeed have to drop its current use of DEFER but this also is unlikely to happen during the SR cycles.

For now it might be best to address this issue in the particular instance we have of the Java Editor.

Dani, I'm begging dude, can you not see if you can fix this on your side ?  I realize that I'm trying to fix the symptom not the disease here but I see no other solution and agree that this is a serious regression.

Should other defects of a similar kind come in then we can address them separately.
Comment 47 Dani Megert CLA 2012-08-20 09:46:31 EDT
(In reply to comment #46)
> Dani, I'm begging dude, can you not see if you can fix this on your side ? 

Sure. Fixed with http://git.eclipse.org/c/platform/eclipse.platform.text.git/commit/?id=d40eeb927576256d757aeded761266f550b22ef2

This adds a workaround that should cover all textual editors. Once we see it's working fine in 4.3, I'll backport it.
Comment 48 Markus Keller CLA 2012-08-20 11:53:44 EDT
(In reply to comment #47)
> http://git.eclipse.org/c/platform/eclipse.platform.text.git/commit/
> ?id=d40eeb927576256d757aeded761266f550b22ef2

This fix looks wrong, since it just calls shell.setLayoutDeferred(false), but doesn't undo its action by calling shell.setLayoutDeferred(true) the same number of times.

It effectively leaves the layout undeferred. Since all correct earlier callers of setLayoutDeferred(true) will eventually call setLayoutDeferred(false), the layoutCount will end up negative. In the end, simple one-level usages of setLayoutDeferred will be ineffective after this.
Comment 49 Eric Moffatt CLA 2012-08-20 13:32:26 EDT
Thanks Dani...

Markus, you've hit on one of the things that Silenio pointed out when we were discussing this issue. The real fear is that as the other folks call 'setDeferredLayouts(false)' themselves to 'unwind' the deferred stack they'll end up making the count go < 0 (a known bug in SWT).

His suggestion was to place the layout into an 'asynchExec', thus guaranteeing that it would be run after the event loop has spun (and purged the pending layouts). It sounds questionable to me since it changes the timing of the layout but if it works...

BTW, I just checked and even calling 'Display.getCurrent.update()' will *not* run the deferred layouts, lending more weight to the idea that SWT may have to take another look at the various interactions taking place here...
Comment 50 Markus Keller CLA 2012-08-20 14:47:18 EDT
Created attachment 220072 [details]
TextViewer workaround 2

Here's the "restoring" I had in mind (patch on top of master).
Comment 51 Dani Megert CLA 2012-08-21 04:41:32 EDT
(In reply to comment #50)
> Created attachment 220072 [details] [diff]
> TextViewer workaround 2
> 
> Here's the "restoring" I had in mind (patch on top of master).

I see your point and like your patch
==> http://git.eclipse.org/c/platform/eclipse.platform.text.git/commit/?id=dec0a5b7df19eade00ea3afb775606885be3f17a

See bug 387669 for the backport.
Comment 52 Eric Moffatt CLA 2012-08-21 13:44:35 EDT
Thanks guys, hopefully before 4.3 goes out we'll have a better solution for this at the SWT level...
Comment 53 Dani Megert CLA 2012-09-03 06:27:34 EDT
*** Bug 388653 has been marked as a duplicate of this bug. ***
Comment 54 Dani Megert CLA 2012-09-11 03:35:10 EDT
*** Bug 389128 has been marked as a duplicate of this bug. ***
Comment 55 Dani Megert CLA 2012-09-13 03:59:09 EDT
*** Bug 389453 has been marked as a duplicate of this bug. ***
Comment 56 Dani Megert CLA 2012-09-14 03:32:53 EDT
*** Bug 389559 has been marked as a duplicate of this bug. ***
Comment 57 Dani Megert CLA 2012-10-08 09:31:24 EDT
*** Bug 391351 has been marked as a duplicate of this bug. ***
Comment 58 Paul Webster CLA 2013-01-24 10:41:35 EST
Eric, can this be moved to 4.3 now?

PW
Comment 59 Eric Moffatt CLA 2013-04-11 15:45:13 EDT
OK, this appears fixed to me. I'm going to mark this as FIXED, feel free to re-open if you still have concerns...
Comment 60 Gary Karasiuk CLA 2013-04-11 16:08:58 EDT
(In reply to comment #59)
It works for me.
Comment 61 Dani Megert CLA 2013-04-12 04:48:39 EDT
(In reply to comment #59)
> OK, this appears fixed to me. I'm going to mark this as FIXED, feel free to
> re-open if you still have concerns...

You already had that "feeling" several times now. Let me repeat two easy test cases that you can try before closing it next time ;-).


Test Case 1:
------------
1. get 'org.eclipse.jface.text' as source in your dev workspace
2. open TextViewer
3. replace the two calls to internalRevealRangeWithWorkaround with
   calls to internalRevealRange
4. save
5. start new target workbench and execute next steps in it
6. close Welcome
7. paste this into Package Explorer:
public class Bug {
// Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug Bug
}
8. Window > New Window
9. Open Type 'Bug'
==> now the bug might appear: the editor is wrongly horizontally scrolled.
If not, repeat step 3 and 4 until it appears. For me this normally happens in less than 3 attempts.


Test Case 2:
------------
1. open the source for ResourceNavigator.java
2. add the following code:
	if (part == ResourceNavigator.this && viewer.getTree().isLayoutDeferred())
		System.out.println("bug!");
to those part listener methods:
    partListener.new IPartListener() {...}.partActivated(IWorkbenchPart)
    partListener.new IPartListener() {...}.partOpened(IWorkbenchPart)

3. start a target workbench
4. close Welcome
5. Window > Show View > Navigator
==>
bug!
bug!

Both test cases pass fine on 3.x.
Comment 62 Eric Moffatt CLA 2013-04-25 15:01:57 EDT
Dani, I'm moving this to 4.4...

We know what the issue(s) are:

1) The new 4.x code makes extensive use of SWT.DEFER

-and-

2) SWT's handling of this type of layout has serious issues (i.e. effectively blocks layouts which are not expected to be deferred).

The only two possibilities for a proper fix require either that we rewrite all the current layout code in the workbench to avoid the use of SWT.DEFER or to have a fix from SWT that fixes the current blocking behavior.

It's too late to change the workbench and I strongly suspect that it's also too late to provide the necessary SWT fix as well (Silenio ?).

At this point it seems that the user community at large is not seeing the effects of this defect thanks to your workaround so it shouldn't appear as a regression to our users...
Comment 63 Dani Megert CLA 2013-08-22 08:02:01 EDT
*** Bug 415546 has been marked as a duplicate of this bug. ***
Comment 64 Eric Moffatt CLA 2013-12-10 15:37:28 EST
M4 is done...
Comment 65 Dani Megert CLA 2014-11-10 05:26:30 EST
Reducing severity based on comment 62.
Comment 66 Stefan Xenos CLA 2015-08-04 13:54:08 EDT
Assigning to myself for investigation. We definitely shouldn't be setting scroll bars to the wrong spot, but we also shouldn't be doing synchronous layouts due to the performance cost.

Going to see if I can find a solution which is both correct and fast.
Comment 67 Dani Megert CLA 2015-08-04 17:48:01 EDT
(In reply to Stefan Xenos from comment #66)
> Assigning to myself for investigation. We definitely shouldn't be setting
> scroll bars to the wrong spot, but we also shouldn't be doing synchronous
> layouts due to the performance cost.
> 
> Going to see if I can find a solution which is both correct and fast.

The first thing to do here is writing tests that assert correct event sequences.
Comment 68 Stefan Xenos CLA 2015-08-05 14:38:18 EDT
> The first thing to do here is writing tests that assert correct
> event sequences.

I disagree with the claim that performing a synchronous layout prior to firing the event is the only way to fix this, so I wouldn't want to write a test asserting that this is the case.

Without having dug into this yet, the following general strategies often work for this type of problem:

A. Lazily perform a partial synchronous layout on demand in order to supply the requested information.
B. Defer the operation until after the layout has completed rather than performing it synchronously.
C. Move some code out of listeners and into custom layout implementations such that it executes as part of the layout pass
D. Some combination of the above

None of these would require forcing a synchronous layout prior to firing the event and I'd like to rule them out before considering a solution with a heavy performance cost.
Comment 69 Dani Megert CLA 2015-08-05 14:43:31 EDT
(In reply to Stefan Xenos from comment #68)
> > The first thing to do here is writing tests that assert correct
> > event sequences.
> 
> I disagree with the claim that performing a synchronous layout prior to
> firing the event is the only way to fix this, so I wouldn't want to write a
> test asserting that this is the case.


When a client gets the listener notification that the part is open/on top, then he can assume to safely call methods on SWT. Those methods will only work if no deferred layouts are pending. That's all what the test should do. Anything that would require the client to do something prior to calling the SWT methods would be wrong.
Comment 70 Stefan Xenos CLA 2015-08-05 14:55:42 EDT
> When a client gets the listener notification that the part is open/on top, 
> then he can assume to safely call methods on SWT. 

I'll respond to this once I've had a chance to investigate further.
Comment 71 Patrik Suzzi CLA 2015-11-11 16:54:51 EST
Created attachment 257885 [details]
Bug tested (case1)

(In reply to Dani Megert from comment #69)
> (In reply to Stefan Xenos from comment #68)
> > > The first thing to do here is writing tests that assert correct
> > > event sequences.
> > 
> > I disagree with the claim that performing a synchronous layout prior to
> > firing the event is the only way to fix this, so I wouldn't want to write a
> > test asserting that this is the case.
> 
> 
> When a client gets the listener notification that the part is open/on top,
> then he can assume to safely call methods on SWT. Those methods will only
> work if no deferred layouts are pending. That's all what the test should do.
> Anything that would require the client to do something prior to calling the
> SWT methods would be wrong.

Dani, according comment #61, I'm able to reproduce the error in both Test Case 1 (see image) and 2 (Navigator is now deprecated).

According Eric's comment #62, we should better understand the extensive use of SWT.DEFER, and ask the SWT guys to point us to the "best practices" for having propert SWT layouting. 

At this point we could follow Stefan's comment #69, by choosing to: (a) perform a sync layout request; (b) defer the layout operation and (c) implement a custom layout. 

As test, I could think to add a plugin contributing a view, to be displayed by a command (like in Navigator). 
Then in the test listen to to partOpened(), partBroughtToTop() and partActivated(), to verify the layout is NOT deferred. 

In the worst case, see Eric's comment #62, we could ask SWT guys to provide us an hook (API) to get synced (wait?) for a deferred layout :)

To better understand, I'd like to ask you some questions:
1. Could you please give me a reference on "best practices" for "proper" layouting?
2. How would you suggest to sync with deferred layouts?
3. Do you know whether there is already a test, similar to the one I described, that I can reuse to verify the bug?

Kind Regards.
Comment 72 Stefan Xenos CLA 2015-11-11 22:38:41 EST
> ask the SWT guys to point us to the "best practices" for having proper SWT layouting

I'm probably the guy you want to ask. I did a lot of work on the SWT layout system and wrote the pseudocode that SWT.DEFER was based on. Sorry, I've been planning (and really looking forward) to fix this one, but my users have been keeping me so busy with JDT core stuff that I haven't had a chance to come back to it.


Why are deferred events desirable?

When you do something like switch perspectives, there will be a whole bunch of part events fired and a whole bunch of views and editors realized. If each one forces a synchronous layout, you end up doing a bunch of layouts when you could have done exactly one. You basically multiply the cost of the layout by the number of views.

The SWT.DEFER thing marks all the controls that need to be recomputed and lays them out in a single pass. This is massively more efficient. However, in order to work properly, anything which positions a control needs to happen as part of the layout pass.

Actually, that's not just due to deferred layouts. It's actually true all the time, but when using deferred layouts the consequences are much more visible. Even without deferred layouts, methods like getBounds() return the result from the most recent layout pass, not the currently-running layout pass... so if you're making a decision about a widget position based on it, your calculations are going to be slightly out of date. The results are particularly noticable when using deferred layouts, though, because the previous layout pass may have never happened so you'll get answers that are wildly off rather than just a few pixels off.

I was planning to address this using a custom layout implementation which bases its decisions on the results of computeSize (which returns a hypothetical size that is always up-to-date), not the result of getBounds() or getSize() (which is always slightly out-of-date).


If you're stumped and you want a rich set of layout examples, check out the Android source code. All the layouts are open-source and the layout algorithm is very similar to SWT's. However, in Android all layouts are deferred - there is no option to layout synchronously, so the source code is full of useful idioms for this sort of thing. An Android RecyclerView also faces this kind of problem -- the view gets instantiated synchronously and its owner may ask it to scroll to a particular row immediately after it is created, before the first asynchronous layout pass runs and the dimensions of the view or its contents are known. The code won't port directly, but it may be a useful source of inspiration since it has so many similarities to SWT.

(Instead of a computeSize(...) that returns a Point, they have a method called measure(...) which returns stores its result in the receiver. You can access it by calling getMeasuredWidth/Height()...).

In the case of Android, most application developers are unaware that the layouts are all asynchronous since they have useful helper methods like "scrollToPosition" that take a row number and figure out the pixel position on the next layout pass. IMO, SWT could offer similar helper methods so that each view and editor doesn't need to write a custom layout to make everything work correctly.


Here's the code for it (LinearLayoutManager.java does the asynchronous magic which turns a synchronous method called by client code into something asynchronous that works with the deferred layout system):

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/support/v7/widget/LinearLayoutManager.java

Notice that scrollToPosition remembers a "pending" scroll position independently of the actual control size and triggers an asynchronous layout. During the asynchronous layout (onLayoutChildren is very similar to SWT's "layout" method), it then figures out the actual pixel position for the scroll as the children are being layed out.

iOS, QT, and every other layout system I've used does something very similar, but the code isn't as easy to read if you're used to SWT.

I believe the same general approach will work here.

Anyway. I'm lurking in the #eclipse-dev IRC channel most days if you'd like to discuss this with me... and if you don't end up fixing this, I promise I will - eventually - as soon as I'm done fixing all my critical JDT core bugs. :-)
Comment 73 Stefan Xenos CLA 2015-11-12 21:25:20 EST
Patrik, I wanted to follow up on our discussion on IRC.

There was a suggestion to use readAndDispatch to force the deferred layout to happen. It will probably do that, but it will also execute unknown code that was scheduled via asyncExec and will eventually cause a hard-to-reproduce crash when one of those asyncExecs tries to close the very part you're opening or corrupts the workbench state by activating a different part. Don't do this.

There was another suggestion to defer the work using asyncExec. Deferring the work is a good idea, but doing so with asyncExec won't work 100% of the time. It would be a race condition since there's no guarantee that your asyncExec will run after the deferred layout. Don't do this either. If I'm reading the above comments correctly, it looks like someone tried this and hit exactly this race.

Instead, defer the work using a custom layout's Layout#layout method. This will always run at exactly the right time (as part of the next layout pass)... and running as part of the layout pass rather than after it is even better since - if you do it right - you'll only reposition the child widgets once rather than twice.
Comment 74 Patrik Suzzi CLA 2015-11-13 02:44:33 EST
Taking the bug, I plan to solve this using a custom Layout#layout
Comment 75 Stefan Xenos CLA 2015-11-13 11:50:05 EST
> Taking the bug, I plan to solve this using a custom Layout#layout

Good to hear. Let me know what you come up with. Once we understand the problem better, it may make sense to push the solution up to SWT so that others who face similar problems won't have to do such an elaborate fix.
Comment 76 Stefan Xenos CLA 2016-01-15 11:54:56 EST
Patrik: Are you still working on this? Anything I can do to help?
Comment 77 Patrik Suzzi CLA 2016-01-17 23:59:30 EST
Stefan, 

I re-started working on this after a long pause. 
- I reproduced the bug, by get+editing org.eclipse.jface.text.TextViewer class
(eclipse.platform.text\.git)

As we agreed in IRC, I should create a new custom layout and attach the custom layout to TextViewer. 

I wasn't very clear how to create and assign the custom layout, 
so I logged the layout responsible of this bug, that is :
org.eclipse.e4.ui.workbench.renderers.swt.TrimmedPartLayout
(eclipse.platform.ui\.git)

This layout is instantiated once, at startup, for each workbench window, in:
WBWRenderer.createWidget(MUIElement, Object) line: 376	

Construction and assignment are as you can see: 

	TrimmedPartLayout tl = new TrimmedPartLayout(wbwShell);
	// ..
	wbwShell.setLayout(tl);

So, according what we agreed, I should create a new layout (#1, #2) 
then replace the TrimmedPartLayout, unless we want to just "edit" it, 
instead of creating a new one. 

To be honest, I'm more for editing the existing TrimmedPartLayout, 
instead of creating a new one.

Thanks for giving your time.
Any suggestion is welcome. 



#1 http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html
#2 http://www.java2s.com/Tutorial/Java/0280__SWT/CreatingYourOwnLayouts.htm
Comment 78 Lars Vogel CLA 2016-04-20 12:14:00 EDT
Mass move to 4.7 as M7 is approaching. Please move back in case you are planning to fix it for Neon.
Comment 79 Dani Megert CLA 2018-05-24 13:01:22 EDT
Moving target milestone to 4.9 for all bugs that are major or above.
Comment 80 Dani Megert CLA 2018-12-04 11:36:58 EST
Moving target milestone to 4.11 for all bugs that are major or above.
Comment 81 Lars Vogel CLA 2019-02-19 03:31:05 EST
Mass change, please reset target if you still planning to fix this for 4.11.
Comment 82 Kalyan Prasad Tatavarthi CLA 2019-05-28 05:47:35 EDT
Please set the target milestone back to 4.12 if you still intend to fix this for 4.12.
Comment 83 Eclipse Genie CLA 2021-05-18 08:11:28 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.