Bug 13467 - [Tasks] Resizing table view should resize columns proportionally?
Summary: [Tasks] Resizing table view should resize columns proportionally?
Status: RESOLVED INVALID
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 2.0   Edit
Hardware: PC Windows 2000
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Platform-UI-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: needinfo
: 2593 4505 4518 (view as bug list)
Depends on:
Blocks: 19780 93611
  Show dependency tree
 
Reported: 2002-04-10 18:18 EDT by Kevin McGuire CLA
Modified: 2009-08-30 02:10 EDT (History)
10 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Kevin McGuire CLA 2002-04-10 18:18:40 EDT
When you resize a table view (e.g. the CVS Resource History View), the columns 
aren't resized proportionally; only the visibility of the columns is affected.  
This is a problem in particular when the view opens as a fast view and the 
columns are all squished together, like what happens with the CVS Resource 
History View.  Having to then manually stretch out the view and every column is 
annoying for people.

A more complex solution would be to be able to specific a suggested initial 
size for a view, and the fast view would attempt to honour that width.  I 
believe Nick discussed this with me once.

However, the simpler answer for now might be that resize of the view could grow 
the columns proportionally.  I assume all table views would appreciate this 
behaviour, as its generally unlikely that they would prefer the clipping that 
occurs now?
Comment 1 Nick Edgar CLA 2002-04-10 21:38:18 EDT
I agree this is the desired behaviour.

However, if you try to adjust the width of TableColumns dynamically in 
response to Table resize events, you currently get major flicker, scrollbars 
flash on and off while resizing, and occasionally stick around after when not 
needed.  This is why we never went this route for the Tasks view.
Need SWT support for this.

Workbench will be adding support to let you specify the preferred width of a 
fast view (as a fraction), which will reduce the impact of this problem.

Moving to SWT for future table work.
Comment 2 Mike Wilson CLA 2002-04-11 09:46:00 EDT
What is the state of table resizing? I thought we fixed some of the problems 
already.
Comment 3 Veronika Irvine CLA 2002-04-11 10:46:09 EDT
Because the scrollbars pop up before the resize callback is sent and the 
columns are resized based on the client area which does not include the 
scrollbars, there is flashing.  Effectively the columns are resized so that the 
horizontal scrollbar is no longer required and then another callback is issued 
because the client area grows when the scrollbars disappear.

Steve gave Nick a hack - see http://dev.eclipse.org/bugs/show_bug.cgi?id=4505 - 
which mostly fixed the problem but was untested on motif and still had one 
condition where a single flash occurs.

Should we pick this up again and try to verify the hack?
Comment 4 Mike Wilson CLA 2002-04-11 13:17:08 EDT
Kevin? Nick?
Comment 5 Nick Edgar CLA 2002-04-11 14:16:04 EDT
It would be interesting, but not essential, for M6 to see if we can make this 
work.
I would appreciate it if the SWT team could come up with an example that works 
on all platforms.
Comment 6 Veronika Irvine CLA 2002-04-12 09:43:05 EDT
The following example shows a platform independant way to get the desired 
result.  This example makes the first column use 1/3 of the space and the 
second column uses the rest but it could easily be modified to support many 
more columns with some weighting strategy.

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

public class TableResizing {
	
	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setLayout(new GridLayout());
		Button b = new Button(shell, SWT.PUSH);
		b.setText("Button");
		
		final Composite comp = new Composite(shell, SWT.NONE);
		comp.setLayoutData(new GridData(GridData.FILL_BOTH));
		final Table table = new Table(comp, SWT.BORDER | SWT.V_SCROLL);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		final TableColumn column1 = new TableColumn(table, SWT.NONE);
		column1.setText("Column 1");
		final TableColumn column2 = new TableColumn(table, SWT.NONE);
		column2.setText("Column 2");
		for (int i = 0; i < 10; i++) {
			TableItem item = new TableItem(table, SWT.NONE);
			item.setText(new String[] {"item 0" + i, "item 1"+i});
		}
		comp.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				Rectangle area = comp.getClientArea();
				Point preferredSize = table.computeSize
(SWT.DEFAULT, SWT.DEFAULT);
				int width = area.width - 2*table.getBorderWidth
();
				if (preferredSize.y > area.height) {
					// Subtract the scrollbar width from 
the total column width
					// if a vertical scrollbar will be 
required
					Point vBarSize = table.getVerticalBar
().getSize();
					width -= vBarSize.x;
				}
				Point oldSize = table.getSize();
				if (oldSize.x > area.width) {
					// table is getting smaller so make the 
columns 
					// smaller first and then resize the 
table to
					// match the client area width
					column1.setWidth(width/3);
					column2.setWidth(width - 
column1.getWidth());
					table.setSize(area.width, area.height);
				} else {
					// table is getting bigger so make the 
table 
					// bigger first and then make the 
columns wider
					// to match the client area width
					table.setSize(area.width, area.height);
					column1.setWidth(width/3);
					column2.setWidth(width - 
column1.getWidth());
				}
			}
		});
		
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}

}
Comment 7 Nick Edgar CLA 2002-04-13 09:51:03 EDT
Nice.  It looks great.
Another important aspect of this approach is that resizing the columns should 
not make the scrollbars disappear.  A good approach is to resize the column to 
the left of the sash (but none further left), and shrink all the columns to 
the right of the sash proportionally.
I had a lot of trouble trying to get this to work previously for many of the 
same reasons as the basic resizing: the OS would add the scrollbars before I 
had the chance to adjust the other columns so they wouldn't be necessary.
Could a similar approach be used to do this?
Comment 8 Veronika Irvine CLA 2002-05-15 07:21:12 EDT
*** Bug 4518 has been marked as a duplicate of this bug. ***
Comment 9 Veronika Irvine CLA 2002-05-15 07:21:59 EDT
*** Bug 4505 has been marked as a duplicate of this bug. ***
Comment 10 Veronika Irvine CLA 2002-05-15 07:23:11 EDT
*** Bug 2593 has been marked as a duplicate of this bug. ***
Comment 11 Veronika Irvine CLA 2002-05-15 16:19:55 EDT
Nick,

I am sending this back to you since I think we have provided an acceptable work 
around.  It is up to you guys if you want to use it.
Comment 12 Nick Edgar CLA 2002-05-15 19:47:41 EDT
As mentioned above, resizing the columns is an important aspect of this.
Can this be done without flicker as well?
Comment 13 Mike Wilson CLA 2002-05-16 10:32:35 EDT
Column resizing works as it always did. You need to manage the 
semantics of what this means under the impact of table resizing yourself.
Comment 14 Nick Edgar CLA 2002-05-16 15:31:32 EDT
The problem is that when resizing the control smaller, scrollbars appear before 
I have the chance to respond to the resize callback on the table.
I suppose that if I do the same as above and resize the columns before resizing 
the table, I can avoid this.  I will have to try.
Please let me know whether you think this should work.
Comment 15 Mike Wilson CLA 2002-05-16 16:00:42 EDT
If you look carefully at the example you will see that it handles the growing 
smaller case by doing exactly what you suggest.
Comment 16 Tod Creasey CLA 2002-07-10 11:06:06 EDT
I have implemented a new ControlAdapter based on the code provided by Veronika 
and I have tried it out with a sample properties page that I whipped up. I 
would suggest that we add this adapter to org.eclipse.ui and deprecate the 
TableLayout so as to encourage people to move to it.

I will release it once approved.

Here is the suggested code:

package org.eclipse.ui.propertiespagetest.properties;

import java.util.ArrayList;

import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;

/**
 * The TableResizeAdapter is a ControlAdapter used to set the size of
 * a table. It is defined on a Composite which is the parent of a table.
 * The Composites only child is the table.
 */
public class TableResizeAdapter extends ControlAdapter {

	private Table table;

	/**
	 * Create a new instance of the receiver with the table to create
	 * specified
	 */
	public TableResizeAdapter(Table tableChild) {
		table = tableChild;
	}

	/**
	 * The list of column layout data (element type:
	 *  <code>ColumnLayoutData</code>).
	 */
	private ArrayList columns = new ArrayList();

	/**
	* Adds a new column of data.
	*
	* @param data the column layout data
	*/
	public void addColumnData(ColumnLayoutData data) {
		columns.add(data);
	}

	/**
	* Sent when the size (width, height) of a control changes.
	* The default behavior is to do nothing.
	*
	* @param e an event containing information about the resize
	*/
	public void controlResized(ControlEvent e) {

		Rectangle area = table.getParent().getClientArea();
		int width = area.width - 2 * table.getBorderWidth();
		
		// XXX: Layout is being called with an invalid value the first 
time
		// it is being called on Linux. This method resets the
		// Layout to null so we make sure we run it only when
		// the value is OK.
		if (width <= 1)
			return;

		

		TableColumn[] tableColumns = table.getColumns();
		int size = Math.min(columns.size(), tableColumns.length);
		int[] widths = new int[size];
		int fixedWidth = 0;
		int numberOfWeightColumns = 0;
		int totalWeight = 0;

		// First calc space occupied by fixed columns
		for (int i = 0; i < size; i++) {
			ColumnLayoutData col = (ColumnLayoutData) columns.get
(i);
			if (col instanceof ColumnPixelData) {
				int pixels = ((ColumnPixelData) col).width;
				widths[i] = pixels;
				fixedWidth += pixels;
			} else if (col instanceof ColumnWeightData) {
				ColumnWeightData cw = (ColumnWeightData) col;
				numberOfWeightColumns++;
				// first time, use the weight specified by the 
column data, otherwise use the actual width as the weight
				// int weight = firstTime ? cw.weight : 
tableColumns[i].getWidth();
				int weight = cw.weight;
				totalWeight += weight;
			} else {
				Assert.isTrue(false, "Unknown column layout 
data"); //$NON-NLS-1$
			}
		}

		// Do we have columns that have a weight
		if (numberOfWeightColumns > 0) {
			// Now distribute the rest to the columns with weight.
			int rest = width - fixedWidth;
			int totalDistributed = 0;
			for (int i = 0; i < size; ++i) {
				ColumnLayoutData col = (ColumnLayoutData) 
columns.get(i);
				if (col instanceof ColumnWeightData) {
					ColumnWeightData cw = 
(ColumnWeightData) col;
					// calculate weight as above
					// int weight = firstTime ? cw.weight : 
tableColumns[i].getWidth();
					int weight = cw.weight;
					int pixels =
						totalWeight == 0 ? 0 : weight * 
rest / totalWeight;
					if (pixels < cw.minimumWidth)
						pixels = cw.minimumWidth;
					totalDistributed += pixels;
					widths[i] = pixels;
				}
			}

			// Distribute any remaining pixels to columns with 
weight.
			int diff = rest - totalDistributed;
			for (int i = 0; diff > 0; ++i) {
				if (i == size)
					i = 0;
				ColumnLayoutData col = (ColumnLayoutData) 
columns.get(i);
				if (col instanceof ColumnWeightData) {
					++widths[i];
					--diff;
				}
			}
		}

		for (int i = 0; i < size; i++) {
			tableColumns[i].setWidth(widths[i]);
		}

	}

}


I implemented it for the following properties page:

package org.eclipse.ui.propertiespagetest.properties;

import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.dialogs.PropertyPage;

public class SamplePropertyPage extends PropertyPage {

	/**
	 * Constructor for SamplePropertyPage.
	 */
	public SamplePropertyPage() {
		super();
	}

	private void addFirstSection(Composite parent) {
		
		Composite enclosingComposite = new Composite(parent, SWT.NONE);
		enclosingComposite.setLayoutData(new GridData
(GridData.FILL_BOTH));
		
		GridLayout layout =	new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		enclosingComposite.setLayout(layout);

		
		Table table = new Table(enclosingComposite, SWT.H_SCROLL | 
SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		GridData data = new GridData(GridData.FILL_BOTH);
		table.setLayoutData(data);
			
		table.setHeaderVisible(true);
		TableColumn column = new TableColumn(table, SWT.NULL);
		column.setText("Column 1"); 
		
		column = new TableColumn(table, SWT.NULL);
		column.setText("Column 2");
		
		TableResizeAdapter adapter = new TableResizeAdapter(table);
		adapter.addColumnData(new ColumnWeightData(50, 100, true));
		adapter.addColumnData(new ColumnWeightData(50, 100, true));
		enclosingComposite.addControlListener(adapter);
	
	
	}


	/**
	 * @see PreferencePage#createContents(Composite)
	 */
	protected Control createContents(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		composite.setLayout(layout);
		GridData data = new GridData(GridData.FILL);
		data.grabExcessHorizontalSpace = true;
		composite.setLayoutData(data);

		addFirstSection(composite);
		
		return composite;
	}

	private Composite createDefaultComposite(Composite parent) {
		Composite composite = new Composite(parent, SWT.NULL);
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		composite.setLayout(layout);

		GridData data = new GridData();
		data.verticalAlignment = GridData.FILL;
		data.horizontalAlignment = GridData.FILL;
		composite.setLayoutData(data);

		return composite;
	}


	
	public boolean performOk() {
		
		return true;
	}

}
Comment 17 Veronika Irvine CLA 2002-07-11 11:24:12 EDT
I don't believe TableResizeAdapter fixes the scrollbar flash problem.

In "private void addFirstSection(Composite parent)" you set a layout on the 
enclosingComposite as well as adding the control listener.  You should only add 
the control listener and in the control listener set the size of the table - 
note that it is important where you set the table size - before or after 
setting the column widths, depending on whether the size is increasing or 
decreasing because otherwise you will get scrollbars flashing.  Having two 
mechanisms for managing the size/location of the table is slow and will cause 
scrollbars to flash.  The layout code always runs before the control resize 
callback and therefore can not be used to fix this problem.

enclosingComposite.setLayout(layout);
...
enclosingComposite.addControlListener(adapter);
Comment 18 Tod Creasey CLA 2006-04-07 10:05:53 EDT
There are currently no plans to work on this feature
Comment 19 Thomas Schindl CLA 2006-10-08 07:53:39 EDT
(In reply to comment #18)
> There are currently no plans to work on this feature
> 

Maybe a stupid question but wouldn't it be enough to replace last lines where you set the ColumnWidths using:

-----------------8<-----------------
Point oldSize = table.getSize();

if (!(oldSize.x > area.width)) {
    table.setSize(area.width, area.height);
}

table.setRedraw(false);

for (int i = 0; i < size; i++) {
    tableColumns[i].setWidth(widths[i]);
}

table.setRedraw(true);

if (oldSize.x > area.width) {
    table.setSize(area.width, area.height);
}
-----------------8<-----------------

At least in my test case this removes the scrollbars flashing but I must admit that I haven't thought about the whole thing in detail.
Comment 20 Tod Creasey CLA 2006-10-10 07:54:53 EDT
The question for me is do you want them to grow proportionally or do you really just want to see on entry better?

My usual situation is that I resize the description field to take up the white space at the end of the table. The other entries are fine for me.
Comment 21 Thomas Schindl CLA 2006-10-10 08:21:37 EDT
(In reply to comment #20)
> The question for me is do you want them to grow proportionally or do you really
> just want to see on entry better?
> 
> My usual situation is that I resize the description field to take up the white
> space at the end of the table. The other entries are fine for me.
> 

What I'm referring is the AdapterClass which could with my patch used to address bug #93611 or am I missunterstanding something. The thing you are referring to is that we should should add a possiblity to mark a column as the one occuping leaving space.
Comment 22 Thomas Schindl CLA 2006-10-10 08:24:00 EDT
Oh. And what I just forgot to mention is that Veronika's example has one small problem, that when you resize the table slowly vertically the space needed for the scrollbars is removed some pixels before the scrollbar are really made visible.
Comment 23 Denis Roy CLA 2009-08-30 02:10:25 EDT
As of now 'LATER' and 'REMIND' resolutions are no longer supported.
Please reopen this bug if it is still valid for you.