Bug 575259 - [MacOS11] Table with header is incorrectly scrolled
Summary: [MacOS11] Table with header is incorrectly scrolled
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.21   Edit
Hardware: PC Mac OS X
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Alexandr Miloslavskiy CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-08-05 09:45 EDT by Thomas Singer CLA
Modified: 2022-02-19 20:45 EST (History)
5 users (show)

See Also:


Attachments
Snippet (914 bytes, text/plain)
2021-08-05 09:46 EDT, Thomas Singer CLA
no flags Details
Screenshot on MacOS 11 (21.74 KB, image/png)
2021-08-05 09:47 EDT, Thomas Singer CLA
no flags Details
Screenshot on MacOS 11 without setHeaderVisible(true) (21.13 KB, image/png)
2021-08-05 09:49 EDT, Thomas Singer CLA
no flags Details
Screenshot from macOS 11.5.2 (Intel) (20.52 KB, image/png)
2021-10-12 06:03 EDT, Thomas Singer CLA
no flags Details
Snippet to show misalignment with the patch (1.23 KB, application/octet-stream)
2022-01-11 06:42 EST, Lakshmi P Shanmugam CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Singer CLA 2021-08-05 09:45:39 EDT
Please launch attached snippet on an M1 Mac with the aarch64 SWT jar. The first visible line will show "2" instead of "0". You have to scroll to see the lines "1" and "0".
Comment 1 Thomas Singer CLA 2021-08-05 09:46:19 EDT
Created attachment 286885 [details]
Snippet
Comment 2 Thomas Singer CLA 2021-08-05 09:47:14 EDT
Created attachment 286886 [details]
Screenshot on MacOS 11
Comment 3 Thomas Singer CLA 2021-08-05 09:48:35 EDT
Commenting the line

  table.setHeaderVisible(true);

will show the line "0" initially.
Comment 4 Thomas Singer CLA 2021-08-05 09:49:02 EDT
Created attachment 286887 [details]
Screenshot on MacOS 11 without setHeaderVisible(true)
Comment 5 Thomas Singer CLA 2021-10-12 06:03:38 EDT
Created attachment 287303 [details]
Screenshot from macOS 11.5.2 (Intel)
Comment 6 Alexandr Miloslavskiy CLA 2021-11-17 20:38:56 EST
I'm currently investigating this.

So far I found that the scroll amount is the sum of heights of Shell's title bar and Table's header.
Comment 7 Eclipse Genie CLA 2021-12-10 20:37:41 EST
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/188761
Comment 9 Eclipse Genie CLA 2022-01-04 09:09:46 EST
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/189026
Comment 11 Eclipse Genie CLA 2022-01-04 09:35:16 EST
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/189027
Comment 12 Lakshmi P Shanmugam CLA 2022-01-04 09:40:38 EST
(In reply to Eclipse Genie from comment #11)
> New Gerrit change created:
> https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/189027

Reverted for M1 and created revert commit to be pushed post M1.
Comment 13 Lakshmi P Shanmugam CLA 2022-01-11 06:42:39 EST
Created attachment 287812 [details]
Snippet to show misalignment with the patch

Due to Bug 577947, I investigated the effect of the patch further in SWT.

It's a UI change that also breaks/changes the existing behaviour of SWT APIs: Table & Tree.getLocation() and getBounds(). When no explicit location is set, the Tree/Table is expected to be placed at location (0,0) inside the parent. For example: Placing a single Control in a Composite without explicit location or layout is a supported use case.

Please see the attached snippet.
Without the patch, the Table is created at location (0,0) inside the Composite. But, with the patch, the Table is created at location (100, 100). getBounds() and getLocation() also return the new values.

Making this change will be considered as API breakage by SWT clients as it can easily break client code.
Comment 14 Thomas Singer CLA 2022-01-11 07:55:17 EST
The question is whether this is already an officially supported and desired behavior or just an undocumented side effect.
Comment 15 Alexandr Miloslavskiy CLA 2022-01-11 09:12:51 EST
> Placing a single Control in a Composite without explicit location or layout is a supported use case.

Thanks for testing! Indeed, this sounds like a strong enough point.

I have already spent way too much time investigating macOS behavior for this patch :( I think that I'm not prepared to spend even more time trying to eliminate the side effects. For now, we'll keep the patch in our own fork. If someone is willing to continue my work and invent a patch without side effects, please do.

I summarized a lot of research in patch's commit message, I'll put a copy here:
----
Various problem-related observations from my research:
1) For a Table contained in a Shell, view hierarchy (relevant part)
   looks like this:
   ------------
   NSThemeFrame               = [Shell.view superview]
	 SWTCanvasView            = Shell.view
	   SWTScrollView          = Table.scrollView
		 NSClipView           = [Table.view superview]
		   SWTTableView       = Table.view
		 NSClipView           = [Table.headerView superview]
		   SWTTableHeaderView = Table.headerView
   ------------
2) The first sub-problem is internal macOS code in
   `-[NSScrollView _updateTitlebarAdjacencyState]`. This wasn't present
   before macOS 11. To my understanding, it's part of transparent
   titlebar feature (see [1]; SWT doesn't use this).
3) (2) contains code like:
   [self setContentInsets: [self safeAreaInsets]]
4) When (2) is called before Table becomes visible, macOS for some
   reason considers Table to be partially under the titlebar. This
   causes `[self safeAreaInsets]` to return the height of Shell's
   titlebar, and this is passed to `[self setContentInsets:]`.
5) I noticed that (4) only occurs when `[SWTCanvasView isFlipped]`
   returns `YES`, like in SWT. In native snippets, it returns `NO`
   by default. With `NO`, macOS no longer thinks that Table is under
   the titlebar and entire sub-problem (2) goes away. I wonder if
   this is a macOS bug?
6) Since macOS 11, `-[NSScrollView setContentInsets:]` contains new
   code that calls `[self tile]` and then scrolls by the difference
   of `[self->_contentView contentInsets]` before and after tiling.
   I understand that this is purely an aesthetic feature intended to
   keep painted content in place while insets are being changed.
   This code can be disabled by setting `NO` to undocumented app
   config option `NSScrollViewAdjustForContentInsetsChange`. When
   disabled, entire sub-problem (2) goes away.
7) I noticed that when Table doesn't have a header, then (6) will
   adjust scrolling twice: first when insets change (0 -> tile_height)
   and second time when insets change back (tile_height -> 0) because
   macOS no longer thinks that Table is under the titlebar. This
   causes two scrolls that compensate each other. However, when Table
   does have a header, macOS gets confused further and insets change
   (1 -> 1) and (tile_height + header_height -> header_height), causing
   unbalanced scroll by (tile_height) pixels. I didn't investigate this
   further.
8) As a result of (2)(3)(4)(6), Table gets scrolled by the height
   of titlebar in first part of the problem.
9) The second sub-problem is related to enabling header, which calls
   `-[NSClipView setContentInsets:]` (note that it sets insets to a
   different view compared to first problem). The inset value in this
   case is the height of the header for `[Table.view superview]`, which
   actually makes sense because header occupies some space in the view.
10) `-[NSClipView setContentInsets:]` also causes Table's scroll range
   to begin from (-insets.top) instead of from 0. That is, when
   scrolled to very top, scroll position is no longer 0 but
   (-insets.top).
11) Before macOS 11, `-[NSTableView tile]` contained code to compensate
   (10) by scrolling table (so that the same row is seen after the
   header after it was shown). Since macOS 11, this code is removed and
   header now obscures top row when shown. This is probably seen as
   aesthetic improvement that reduces changes on screen. What this
   means is that since macOS 11, showing header causes Table to appear
   scrolled by the height of the header (in fact, scroll position stays
   the same while internal coordinates are moved).
12) As a result of (9)(10)(11), Table appears scrolled by the height of
   its header.
13) As a result of (8)(12), Table appears scrolled twice: by the total
   amount of (titlebar_height + table_header_height).
14) While debugging all that and comparing SWT to native snippet I have
   composed, I noticed that giving Table an initial non-zero
   `[NSScrollView setFrame:]` helps. After some experimenting, I found
   that the best rect is such that it has space for table's header even
   after macOS removes the portion of it that it assumes to be obscured
   by title bar (that is, y coordinates from 0 to titlebar_height).
15) (14) looked like the least evil workaround, so I grabbed it. Also,
   this workaround is kind of intuitive: I could say that problem is
   caused by "overlapping" titlebar and header that doesn't fit. By
   that time I already spent way too much time researching, so I
   didn't try to study macOS any further.

Other workarounds considered:
1) Disable `NSScrollViewAdjustForContentInsetsChange`
   This successfully fixes problem (8). But I didn't like the
   undocumented nature of this workaround. Also, it would apply
   to all views, not just Table and Tree.
2) Hook `[NSScrollView setContentInsets:]` and set
   `Table.shouldScroll = false` for its duration. The goal is to
   suppress `NSScrollViewAdjustForContentInsetsChange` without changing
   it directly.
   This successfully fixes problem (8). Still, it felt dirty to me.
3) Disable `[NSScrollView automaticallyAdjustsContentInsets]`
   I have seen that it's evaluated in
   `-[NSScrollView _updateTitlebarAdjacencyState]`, which triggers
   problem (8). Eventually I didn't try it, because I was suspicious of
   side effects.
4) In SWT `Table.setHeaderVisible()`, scroll by header height
   This is supposed to fix problem (12) by restoring pre-macOS 11
   behavior. Eventually I didn't try it, because (14) suddenly solved
   both problems.

[1] Apple Docs for [NSWindow titlebarAppearsTransparent]
	https://developer.apple.com/documentation/appkit/nswindow/1419167-titlebarappearstransparent?language=objc
----
Comment 16 Thomas Singer CLA 2022-02-08 10:04:55 EST
Do I understand it correctly, that this (initial) problem does not occur in Eclipse?
Comment 17 Alexandr Miloslavskiy CLA 2022-02-08 17:02:09 EST
I understand that the all of these are required for a Table/Tree to be affected:
1) Header is visible
2) Enough items for scrollbar to show
3) Items are created before Table/Tree is shown

I went through some Eclipse panes quickly and found at least these to be affected:
'Preferences > Java > Editor > Templates'
'Preferences > Version Control (team) > File Content'

These Tables are initially scrolled in a confusing way, where a few first rows are under the Table's header.
Comment 18 Thomas Singer CLA 2022-02-09 03:16:34 EST
Then I don't understand the Eclipse team that they reject your patch and don't fix these parts where a layout manager is missing by simply setting one or setting the bounds instead of the size.
Comment 19 Alexandr Miloslavskiy CLA 2022-02-09 16:24:51 EST
As described in Comment 13, "Placing a single Control in a Composite without explicit location or layout is a supported use case".

My patch would break this use case. Unfortunately patch is not a fix, but only a workaround, and it has side effects.
Comment 20 Andrey Loskutov CLA 2022-02-17 08:57:54 EST
Alexandr, if I understood it right, there is no solution for the moment? If yes, please update the "Target Milestone".