How to always show vertical scroll bar in SWT table?
Asked Answered
W

3

6

Is it possible to always show the vertical scroll bar in a SWT table even if the table is empty? By always showing a (possible disabled) vertical scroll bar one can avoid that the last column get partially hidden when the columns use ColumnWeightData for layouting.

I tried to initialize the table with SWT.V_SCROLL or to use table.getVerticalBar().setVisible(true) - both without success.

There is a method setAlwaysShowScrollBars in ScrollableComposite. What I am looking for is a similar method in Table.

UPDATE: I suppose that the scroll bars which are visible when the table contains enough data are not those scroll bars which Table inherits from Scrollable. I have debugged ScrollBar.setVisible(boolean) and it seems not be called on table layout updates. Is this observation correct?

UPDATE 2: Here is a snippet for a table construction. It would be great to have the vertical scrollbar visible even if the table is empty and to have the column headers visible even if the table data are scrolled down. Note: The snippet has left out some details as the label provider and some other controls arranged at the same parent composite.

protected void createMasterPart(final IManagedForm managedForm, Composite parentComposite)
{
  FormToolkit toolkit = managedForm.getToolkit();

  Composite contentComposite = toolkit.createComposite(parentComposite, SWT.NONE);
  contentComposite.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
  toolkit.paintBordersFor(contentComposite);

  contentComposite.setLayout(new GridLayout(2, false));
  GridData gd;

  Composite tableComposite = new Composite(contentComposite, SWT.NONE);
  TableColumnLayout tableColumnLayout = new TableColumnLayout();
  tableComposite.setLayout(tableColumnLayout);
  gd = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 3);
  tableComposite.setLayoutData(gd);

  speakerTableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION);
  speakerTableViewer.setContentProvider(ArrayContentProvider.getInstance());
  Table speakerTable = speakerTableViewer.getTable();
  speakerTable.setHeaderVisible(true);
  speakerTable.setLinesVisible(true);
  toolkit.paintBordersFor(speakerTable);

  TableViewerColumn tableViewerAudiosampleColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
  TableColumn audiosampleColumn = tableViewerAudiosampleColumn.getColumn();
  tableColumnLayout.setColumnData(audiosampleColumn, new ColumnWeightData(60, true));
  audiosampleColumn.setText("Sample");

  TableViewerColumn tableViewerSpeakerColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
  TableColumn speakerColumn = tableViewerSpeakerColumn.getColumn();
  tableColumnLayout.setColumnData(speakerColumn, new ColumnWeightData(60, true));
  speakerColumn.setText("Speaker");

  TableViewerColumn tableViewerRemarkColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
  TableColumn remarkColumn = tableViewerRemarkColumn.getColumn();
  tableColumnLayout.setColumnData(remarkColumn, new ColumnWeightData(120, true));
  remarkColumn.setText("Remark");
}
Wizardly answered 15/10, 2013 at 14:33 Comment(8)
Wrap your table inside panel.Pipe
@Pipe What do you mean by panel?Wizardly
why would you display scrollbar for an empty data table?Pipe
You could use org.eclipse.jface.layout.TableColumnLayout which should adjust the column sizes when the scroll bar appears.Stricture
@Pipe I want to avoid that the last column is partially hidden if at first the table has no scrollbars and later on the scrollbars appear. Moreover, I want to avoid that the columns get resized under such circumstances.Wizardly
@Stricture I already use TableColumnLayout. I want to avoid that the columns get resized as soon as the scrollbar apears.Wizardly
@Michael Updated my answer, please have a look.Vieira
@Michael Updated answer again.Vieira
V
6

It's not possible to force the Table to always show scroll bars, the OS decides when to show them.

Alternatives

Right, I came up with a solution very similar to my answer to this question:

Is it possible to get the vertical/horizontal scroll bar visible when the SWT List is in disabled state?


The idea is to use a ScrolledComposite (as the other answer already suggested) to take care of the scrolling. The Table itself won't scroll. However, this won't make any difference, because the user won't be able to tell the difference.

ScrolledComposite has a method called setAlwaysShowScrollBars(boolean) with which you can force it to always show the scroll bars, even if they aren't required.

Here is some sample code, that will illustrate what I just talked about:

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

    final ScrolledComposite composite = new ScrolledComposite(shell, SWT.V_SCROLL);
    composite.setLayout(new GridLayout());
    composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    final Table table = new Table(composite, SWT.NO_SCROLL | SWT.FULL_SELECTION);
    table.setHeaderVisible(true);

    composite.setContent(table);
    composite.setExpandHorizontal(true);
    composite.setExpandVertical(true);
    composite.setAlwaysShowScrollBars(true);
    composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));

    Button fillTable = new Button(shell, SWT.PUSH);
    fillTable.setText("Fill table");
    fillTable.setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));

    fillTable.addListener(SWT.Selection, new Listener()
    {
        @Override
        public void handleEvent(Event arg0)
        {
            if (table.getColumnCount() < 1)
            {
                for (int col = 0; col < 4; col++)
                {
                    TableColumn column = new TableColumn(table, SWT.NONE);
                    column.setText("Column " + col);
                }
            }

            for (int row = 0; row < 20; row++)
            {
                TableItem item = new TableItem(table, SWT.NONE);

                for (int col = 0; col < table.getColumnCount(); col++)
                {
                    item.setText(col, "Item " + row + " " + col);
                }
            }

            for (int col = 0; col < table.getColumnCount(); col++)
            {
                table.getColumn(col).pack();
            }

            composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        }
    });

    Button clearTable = new Button(shell, SWT.PUSH);
    clearTable.setText("Clear table");
    clearTable.setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));

    clearTable.addListener(SWT.Selection, new Listener()
    {
        @Override
        public void handleEvent(Event arg0)
        {
            table.removeAll();

            composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        }
    });

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

Looks like this:

enter image description here enter image description here

As you can see, the scroll bar is always visible.


UPDATE

As pointed out in the comment, this approach will not keep the Table headers visible when you scroll down. If you could post a small working code example that illustrates your problem, we might come up with an alternative (unrelated to forcing the scroll bars).

UPDATE2

Here is some code that should do what you want, the trick is to trigger a resize event on the parent of the TableViewer, the horizontal scrollbar that is shown isn't really necessary and it disappears after you resize the window:

public static void main(String[] args)
{
    final Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setText("StackOverflow");
    shell.setLayout(new GridLayout());

    createMasterPart(shell);

    shell.pack();
    shell.setSize(400, 300);
    shell.open();

    shell.layout(true, true);

    while (!shell.isDisposed())
    {
        if (!display.readAndDispatch())
            display.sleep();
    }
    display.dispose();
}

private static void createMasterPart(Composite parentComposite)
{
    Composite composite = new Composite(parentComposite, SWT.NONE);
    composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    composite.setLayout(new GridLayout(1, false));

    Composite tableComposite = new Composite(composite, SWT.NONE);
    TableColumnLayout tableColumnLayout = new TableColumnLayout();
    tableComposite.setLayout(tableColumnLayout);
    tableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    TableViewer tableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION);
    tableViewer.setContentProvider(ArrayContentProvider.getInstance());
    Table table = tableViewer.getTable();
    table.setHeaderVisible(true);
    table.setLinesVisible(true);

    TableViewerColumn firstTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
    TableColumn firstTableColumn = firstTableViewerColumn.getColumn();
    firstTableColumn.setText("Sample");
    firstTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
    {
        @Override
        public String getText(Object element)
        {
            Dummy p = (Dummy) element;
            return p.first;
        }
    });

    TableViewerColumn secondTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
    TableColumn secondTableColumn = secondTableViewerColumn.getColumn();
    secondTableColumn.setText("Speaker");
    secondTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
    {
        @Override
        public String getText(Object element)
        {
            Dummy p = (Dummy) element;
            return p.second;
        }
    });

    TableViewerColumn thirdTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
    TableColumn thirdTableColumn = thirdTableViewerColumn.getColumn();
    thirdTableColumn.setText("Remark");
    thirdTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
    {
        @Override
        public String getText(Object element)
        {
            Dummy p = (Dummy) element;
            return p.third;
        }
    });

    List<Dummy> elements = new ArrayList<>();

    for(int i = 0; i < 20; i++)
    {
        elements.add(new Dummy("firstfirstfirst " + i, "secondsecondsecond " + i, "thirdthirdthirdthirdthirdthird " + i));
    }

    tableViewer.setInput(elements);

    tableColumnLayout.setColumnData(firstTableColumn, new ColumnWeightData(1, true));
    tableColumnLayout.setColumnData(secondTableColumn, new ColumnWeightData(1, true));
    tableColumnLayout.setColumnData(thirdTableColumn, new ColumnWeightData(2, true));
}

private static class Dummy
{
    public String first;
    public String second;
    public String third;

    public Dummy(String first, String second, String third)
    {
        this.first = first;
        this.second = second;
        this.third = third;
    }
}
Vieira answered 2/4, 2014 at 8:18 Comment(17)
Are column headers still visible after scroll down?Judo
@Judo They never are in SWT Tables.Vieira
In usual table headers are always shown in every scroll position.Judo
@Judo Huh, never realized. They definitely won't be with this code. OP will have to decide what's more important.Vieira
@Vieira Thanks for your help! However, table headers are more important to me (as the question owner). And yes: the problem seems to be the OS. How can I force the OS to show the scroll bars? Is there a way to set a combination of properties on the table object which let the OS show the scrollbar?Wizardly
@Michael No, there is no way to force the OS to show the scroll bar from SWT code. As I said in the answer, if you can post a (minimal) sample program that shows your issue, we might be able to find a solution that doesn't make fiddling about with the scroll bars necessary. The only way to make the OS show the scroll bars would be to add "empty" TableItems until there are enough of them to make the scroll bars appear, but that's a road you don't want to go down.Vieira
@Vieira I have added a snippet to the original question. Hope this helps. Thank you!Wizardly
@Vieira Give me some time to test it in my enviroment where I use RCP with views and forms. If it works even when the window, the view or the form is resized by the user I will accept your answer. I appreciate you efforts and help!Wizardly
@Vieira I have tested the solution under "Update 2". If I resize the window such that all table items are visible then the vertical scrollbar is hidden. If I start with an empty table the vertical scrollbar is hidden too. I am afraid this is not a solution to the original problem. Thank you for your efforts!Wizardly
@Michael The goal of my code wasn't to hide the vertical scrollbar, but the horizontal one. Can you verify, that my code does not work for you and if so, please elaborate why?Vieira
@Vieira The question was "How to always show vertical scroll bar?" not to hide the horizontal one. I guess (and you already confirmed this) that this is a matter of the OS.Wizardly
@Michael Yes it is up to the OS, but what I'm suggesting in my latest update is an alternative that doesn't require forcing the scroll bar. Did you try it in your environment?Vieira
@Vieira I tried it with the outcome described above. I understand that the vertical scroll bar can't be shown under any circumstances. I see that instead of this your solution ensures that the last column is not partially hidden. However, the question was how to always show the scroll bar. If the scroll bar would be always visible then columns don't get resized when the table overflows. This happens with the solution above. I appriciate your help and that's why I voted up your answer. Let the community decide if the original question is really answered.Wizardly
@Michael Ok, then the answer is: "No", you can't force the scroll bars to always be shown. BTW: how would the community decide if the original question is answered? The person who decides that (and may mark this as answered) is you.Vieira
@Vieira In addition to my last comment I have to note that the solution doesn't work in an RCP environment with views and editors (see one of my comments above). Views are initialized lazily when the workbench is already open. If the shell is packed and resized the workbench gets resized.Wizardly
@Vieira Ok, please make clear at the very top of your answer that it is not possible to always show the scroll bar. After that I will mark it as answer.Wizardly
@Michael Done, sorry that we couldn't find a suitable solution for your case though...Vieira
P
1

I have created a solution that I think is better than put your table inside a ScrolledComposite.

My solution: fill my table with empty items until my scroll bar is visible.

Example:

// Flag that knows if the empty item was added or not
boolean addedEmptyItem = false;

// Get the table client area
Rectangle rect = table.getClientArea ();
// Get the item height 
int itemHeight = table.getItemHeight ();
// Get the header height
int headerHeight = table.getHeaderHeight ();
// Calculate how many items can be visible without scrolling
int visibleCount = (rect.height - headerHeight + itemHeight - 1) / itemHeight;

while ( visibleCount > table.getItemCount() ) {
   // Add an empty item
   new TableItem( table, SWT.NONE );

   // Set the flag
   addedEmptyItem = true;
}

// Vertical bar é disabled if an empty item was added
table.getVerticalBar().setEnabled( !addedEmptyItem );

I hope this solution helps someone.

Thanks.

Precipitous answered 18/5, 2015 at 20:58 Comment(1)
Thanks for the example! However, I am afraid that there is a lot to do to get this hack working in a productive environment: disable selection of empty items; adapt when table is resized; adapt if real items are added; work with table models. Thanks anyway!Wizardly
R
-1

I don't think you can do this but you can try call ScrolledComposite.setAlwaysShowScrollbars() to true, but you will see both of the enabled scrollbars all the time.

Roundhead answered 15/10, 2013 at 14:44 Comment(1)
The table is not a ScrollableComposite and is not inside a ScrollableComposite. What is ScrolledComposite?Wizardly

© 2022 - 2024 — McMap. All rights reserved.