eclipse-platform / eclipse.platform.swt

Eclipse SWT
https://www.eclipse.org/swt/
Eclipse Public License 2.0
101 stars 124 forks source link

[GTK] Table: vertical scrollbar is misplaced depending on used Layout #403

Open tmssngr opened 1 year ago

tmssngr commented 1 year ago

Describe the bug The attached snippet places a single-column table inside a shell and used GridLayout for that. When moving the mouse over the table, it becomes noticeable, that the vertical scrollbar is not on the top though Item 0 is shown. Using FillLayout instead of the GridLayout and this effect does not occur. Most likely the table behaves differently if its preferred size has been requested before by the GridLayout.

To Reproduce

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class TableWithoutHeader {

    public static void main(String[] args) {
        final Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new GridLayout(1, false));

        final Table table = new Table(shell, SWT.VIRTUAL | SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        final Listener listener = event -> {
            final TableItem item = (TableItem)event.item;
            if (event.type == SWT.EraseItem) {
                event.detail &= ~SWT.FOREGROUND;
            }
            else if (event.type == SWT.MeasureItem) {
                final String text = (String)item.getData();
                final Point size = event.gc.textExtent(text);
                event.height = Math.max(event.y, size.y);
                event.width = Math.max(event.width, size.x);
            }
            else if (event.type == SWT.PaintItem) {
                event.gc.drawText((String)item.getData(), event.x, event.y, true);
            }
            else if (event.type == SWT.SetData) {
                final int index = table.indexOf(item);
                final String data = "Item " + index;
                item.setData(data);
            }
        };
        table.addListener(SWT.EraseItem, listener);
        table.addListener(SWT.SetData, listener);
        table.addListener(SWT.MeasureItem, listener);
        table.addListener(SWT.PaintItem, listener);

        final TableColumn column = new TableColumn(table, SWT.LEFT);
        column.setText("");
        column.setResizable(false);
        column.setWidth(100);

        table.setItemCount(30);

        shell.setSize(400, 500);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

Expected behavior The vertical scrollbar should be at the top under all conditions when showing the first item.

Screenshots Screenshot from 2022-09-23 13-22-24

Environment:

  1. Select the platform(s) on which the behavior is seen:

      • [ ] All OS
      • [ ] Windows
      • [x] Linux
      • [ ] macOS
  2. Additional OS info (e.g. OS version, Linux Desktop, etc) Reproduced with latest Manjaro with Gnome 3.24.34, default Adw-dark theme.

  3. JRE/JDK version Java 17

Version since I have not bisected.

Workaround (or) Additional context None.

tmssngr commented 1 year ago

This is the screenshot when using FillLayout instead. Screenshot from 2022-09-23 13-33-05

If using FillLayout and invoking table.computeSize(SWT.DEFAULT, SWT.DEFAULT); before showing the shell, the same behavior as with GridLayout occurs.

mbooth101 commented 1 year ago

I see this on both X11 and Wayland backends.

Here is a simpler reproducer:

public class TableWithoutHeader {

    public static void main(String[] args) {
        final Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new GridLayout(1, false));

        final Table table = new Table(shell, SWT.VIRTUAL | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // XXX This is the tell-tale line
        table.setHeaderVisible(false);

        final TableColumn column = new TableColumn(table, SWT.NONE);
        column.setText("Hello");
        column.setResizable(false);
        column.setWidth(100);

        table.setItemCount(50);

        shell.setSize(400, 500);
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

The bug is that the scrollbar always leaves room for the header even when column headers are set to non-visible. If you change table.setHeaderVisible(false); above to true then you can see the header fits exactly into the space left by the scrollbar.

Potential workaround: Using TableColumn is not mandatory -- if you don't need column headers you can add TableItems directly. See the SWT Snippet 35 for an example of this: https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet35.java

tmssngr commented 8 months ago

It looks like a table.computeSize(SWT.DEFAULT, SWT.DEFAULT); before the table.setHeaderVisible(false); eliminates this misbehavior for me.