How to avoid SWT table enlargement correctly? - java

I have a simple SWT program like this:
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
shell.setMinimumSize(300, 300);
// table
final Table table = new Table(shell, SWT.BORDER);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.heightHint = 0; // "hack"
table.setLayoutData(gridData);
// example data
for (int i = 0; i < 50; i++) {
final TableItem item = new TableItem(table, SWT.NONE);
item.setText("item no." + i);
}
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
What I expect - I want the table to completely fill out the shells space without changing it's size. (Screenshot 1 and 3)
Problem - If I add rows to the table, the table automatically gets resized (and so also the shell). (Screenshot 2)
Workaround - To avoid this behaviour I added the line gridData.heightHint = 0; to the code. But this seems like a hack to me.
Question - What would be the correct way to avoid the enlargement of the table (and the shell) when data is added?
Regards, winklerrr
Screenshot 1
Without data, table and shell don't get resized, correct behaviour, in both versions
Screenshot 2
With data, table and shell are getting enlarged, wrong behaviour, only without the hack
Screenshot 3
With data, table and shell don't get resized, scrollbar is added, correct behaviour, only with the hack

call shell.setSize(int width, int height) at some point before adding data to the table?

You usually expect the table to have a certain logical height, say 20 rows. At least that's an approach I often choose.
To achieve that, I compute the expected initial height in pixels like this and use it as a height hint.
gridData.heightHint = table.getItemHeight() * 20;
To be more accurate, you would also want to add the table's trim.

shell.setSize(int width, int height)
add this code after the data to your table.

Related

What does the moveBelow method of Control class do?

I am using a Gridlayout with 2 column. I have Labels and corresponding Text control with it. I wanted the Text control of first label to slip down the label instead of right next to it (since its a gridlayout). For this I thought the moveBelow method would work but doesn't seem to be. Am i interpreting the use of the method wrongly?
Label label = Components.createLabel(myContainer, SWT.LEFT
| SWT.WRAP);
abel.setText("WC Plan Name");
textName = createTextControl(myContainer, SWT.LEFT);
textName.moveBelow(label);
private Text createTextControl(Composite parent, int horizontalAlignment)
{
final Text textControl = Components.createText(parent, SWT.SINGLE | SWT.BORDER);
final GridData layoutData = new GridData(horizontalAlignment, SWT.FILL, false, false);
layoutData.widthHint = 200;
textControl.setLayoutData(layoutData);
return textControl;
}
moveBelow() does exactly what it says in the documentation:
Moves the receiver below the specified control in the drawing order. If the argument is null, then the receiver is moved to the bottom of the drawing order. The control at the bottom of the drawing order will be covered by all other controls which occupy intersecting areas.
This means that it can be used to reorder children (if the layout of the parent allows it). For example, if you have a RowLayout and call moveBelow(null) on the last child, it will be moved to the top.
Now to solve your problem: You have a GridLayout with 2 columns. A GridLayout is filled from top left to bottom right. If you want two elements to appear below each other rather than next to each other, there are two options:
Add an empty Label in between, so that it can occupy the space to the right of your first element
Add a GridData to your first element and set GridData#horizontalSpan to 2. This way it will span two columns.
UPDATE
Here is an example of solution 2:
public static void main(String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new GridLayout(4, false));
Text text = new Text(shell, SWT.BORDER);
text.setLayoutData(new GridData(SWT.BEGINNING, SWT.TOP, false, true, 4, 1));
text = new Text(shell, SWT.BORDER);
text.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true, 4, 1));
for (int i = 0; i < 8; i++)
{
new Button(shell, SWT.PUSH).setText("Button " + i);
}
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
Looks like this:

How to set max composite size in SWT

Is there any way to set maximal size of composite? Only what i have found methods
setMinimumSize(Point point)
setSize(Point point)
which allow me to set minimal and prefered size.
As far as I know, there is no Layout, that has a setting for maximal size, which is a good thing in my opinion.
Consider the following szenario: You set the maximal size of a Widget/Composite to a value that you think "looks good". Depending on the screen resolution and text size of the end-user, the chosen maximal size might just look wrong. This is why the layouts usually adapt to the available space.
Nevertheless, here is some code, that restricts the size:
public static void main(String[] args)
{
Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new GridLayout(1, false));
final Button left = new Button(shell, SWT.PUSH);
left.setText("Restricted width");
left.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
left.addListener(SWT.Resize, new Listener()
{
#Override
public void handleEvent(Event arg0)
{
Point size = left.getSize();
if(size.x > 200)
{
left.setSize(200, size.y);
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
Before resizing:
After resizing:
i am curious about why you want to set a maximum size. can you give a concrete example? i had a situation where i wanted this option, but i came up with a solution, that was even better. just think of a widget in a layout that has a preferred size based on its contents, e.g. a text widget. now you want that widget to be as big as the available space allows it. but since that widget is claiming space based on its contents, it might claim more space than necessary. so what i needed was a maximum size, but if more space is available, that widget will still be able to take the space without claiming more.
but lets have a look at an example:
public static void main(final String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
final Text text = new Text(shell, SWT.WRAP | SWT.MULTI | SWT.V_SCROLL);
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
text.setText(getLongText());
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
the call to shell.pack() will make the shell use the preferred size to display its contents. the layout will then ask the text widget to compute its preferred size. the text widget does not know, that the shell will be expanded as well and does not have a fixed size.
well this problem can be solved if we could give the text widget a maximum size. but it will cause another problem with the grid layout, since we don't want the text widget to be smaller in case that the shell has a minimum size which is bigger than the preferred size of the text widget. i cannot give you a code example of this case, since there is no maximum size, but just imagine the shell be of a fixed size, and the the text widget inside has a maximum size that is smaller.
the solution is to limit the preferred size computed by the widget to be of a maximum preferred size. i didn't find yet a method to set this value, but you can achieve this effect by overriding the computeSize method of the widget:
final Text text = new Text(shell, SWT.WRAP | SWT.MULTI | SWT.V_SCROLL) {
/** The maximum preferred size. */
private final Point maxSize = new Point(500, 400);
#Override
public Point computeSize(final int wHint, final int hHint, final boolean changed) {
final Point preferredSize = super.computeSize(wHint, hHint, changed);
return new Point(Math.min(maxSize.x, preferredSize.x), Math.min(maxSize.y, preferredSize.y));
}
#Override
protected void checkSubclass() {
// noop, allow subclassing, since we know what we are doing
}
};
now you are still able to increase the shells size by a user drag operation and the widget will still we able to grab more space above the maximum size.

Compute the minimum size of a Composite

I need to calculate the minimum or default size of a Composite where it can display all components without clipping.
I only seem to find methods to calculate the preferred size of the Composite. This means that a Table or other scrolled composite will have a preferred size which displays the full contents without scrolling. The ScrolledComposite will immediately go into scroll mode, which is not what I want.
GridLayout manages to do this by treating the GridData hints as minimum width/height, allowing grabbing any extra space available.
The problem is related to this one: SWT - computingSize for Multi-line textfield inside ScrolledComposite
Control#computeSize(int, int) should be what you are searching for:
Point size = comp.computeSize(SWT.DEFAULT, SWT.DEFAULT);
System.out.println(size.x + " " + size.y);
I've managed to find the solution.
The key was two different things:
Make sure to set the resize listener on both the content (if CHILDREN are added and layout() is called) and the ScrolledComposite (if it is resized from outside its children)
Make sure to set both GridData.grab and GridData.hint. The hint will make sure the composite assumes this size when you do computeSize(), while grab makes sure it will grab any extra space that is available.
Code sample is below:
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell(display);
ScrolledComposite sc = new ScrolledComposite(shell, SWT.NONE);
Composite foo = new Composite(sc, SWT.NONE);
foo.setLayout(new GridLayout(1, false));
StyledText text = new StyledText(foo, SWT.NONE);
text.setText("Ipsum dolor etc... \n etc... \n etc....");
GridDataFactory.fillDefaults().grab(true, true).hint(40, 40).applyTo(text);
Listener l = new Listener() {
public void handleEvent(Event e) {
Point size = sc.getSize();
Point cUnrestrainedSize = content.computeSize(SWT.DEFAULT, SWT.DEFAULT);
if(size.y >= cUnrestrainedSize.y && size.x >= cUnrestrainedSize.x) {
content.setSize(size);
return;
}
// does not fit
Rectangle hostRect = getBounds();
int border = getBorderWidth();
hostRect.width -= 2*border;
hostRect.width -= getVerticalBar().getSize().x;
hostRect.height -= 2*border;
hostRect.height -= getHorizontalBar().getSize().y;
c.setSize(
Math.max(cUnrestrainedSize.x, hostRect.width),
Math.max(cUnrestrainedSize.y, hostRect.height)
);
}
}
sc.addListener(SWT.Resize, l);
foo.addListener(SWT.Resize, l);
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}

SWT RowLayout: what is the minimum size?

I must be missing something stupid. The docs clearly state that the RowData object for the RowLayout layout lets you specify a minimum size (width and height), which makes sense. However, when the underlying widget exceeds this size, the size does not increases and the widget is cropped. Is it really a minimum?
Example
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Example");
shell.setBounds(100, 100, 325, 200);
shell.setLayout(new FillLayout());
Composite comp = new Composite(shell, SWT.BORDER);
comp.setLayout(new RowLayout(SWT.VERTICAL));
Label label1 = new Label(comp, SWT.CENTER);
label1.setLayoutData(new RowData(20,20));
label1.setText("Trying with bounded rowdata...");
Label label2 = new Label(comp, SWT.CENTER);
label2.setText("Trying with no rowdata...");
comp.layout(true,true); // no difference
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
Result:
The javadoc of the constructor RowData(int, int) must be wrong! If you read carrefully the header of the RowData javadoc, it is said that RowData are used to set an initial size for Controls, not the minimum. That's why your Labelis croped by a square of 20x20 px!
Try turning packing off on the layout:
Composite comp = new Composite(shell, SWT.BORDER);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.pack = false;
comp.setLayout(layout);
On the other hand you can also consider using a GridLayout if you want the controls to resize with respect to their parents but not less than a minimum width. The minimum width works with GridData.

SWT - Creating a grid of numbers that I'd like to be able to tab through with keyboard

This problem has been driving me CRAZY!!! Using SWT, I'd like to create a grid of numbers that I can tab traverse using the keyboard. I'd like to be able to select and click into each cell (not just on the numbers) to perform some action. In other words, I'm looking for a way to make a true FLAT button. The goal here is to make the grid accessible with screen readers such that when a cell has focus, the screen reader will read the number value in the middle of each cell.
Originally, I created a GridLayout with a set of button controls to display the number values. This actually worked but I didn't want the buttons to look like buttons. The SWT.FLAT style for button controls doesn't work on Windows operating systems. This is caused by an OS limitation.
Next I then tried converting all buttons to labels but since label controls cant take focus, I couldn't implement any type of tab traversal. Next I tried replacing all labels with read-only text controls. I was able to re-introduce the tab traversal/focusing but I can't get the text to display in the dead-center of each cell. This is actually caused by a SWT limitation. The SWT.CENTER style for text controls only affects the horizontal alignment. Text controls can't be vertically aligned.
So finally, someone told me to wrap each read-only text control in a Composite. This allowed me to center the read-only text controls but I don't know how to make the composite itself to be tab traversed.
Any ideas on how I can get this done or how I can make controls that typically dont take focus (like composite, canvas, label) actually take focus so I can tab traverse each control with my keyboard?
I'm fairly new to java and SWT so I apologize if some of this is confusing. Many thanks. This is how I've constructed each of my cells thus far (I've replaced the read-only text with CLabel controls):
Display display = new Display();
Shell shell = new Shell(display);
GridLayout gridLayout = new GridLayout(5, false);
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
gridLayout.horizontalSpacing = 1;
gridLayout.verticalSpacing = 1;
shell.setLayout(gridLayout);
Composite resetComp = new Composite(shell, SWT.BORDER);
GridData compgridData = new GridData(SWT.CENTER, SWT.CENTER, true, true);
GridData resetGD = new GridData(SWT.FILL, SWT.FILL, false, false);
resetGD.verticalSpan = 2;
resetComp.setLayoutData(resetGD);
resetComp.setLayout(new GridLayout(1, false));
CLabel resetCLabel = new CLabel(resetComp, SWT.SHADOW_OUT | SWT.CENTER);
resetCLabel.setText("Reset"); //$NON-NLS-1$
resetCLabel.setLayoutData(compgridData);
resetCLabel.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); //$NON-NLS-1$
resetComp.setToolTipText(resetCLabel.getText());
Here is a trick, maybe it will suits you: create each button inside a Composite, and set the size of the Button bigger than its parent Composite; This way, the border of button is not visible, because outside of its parent composite.
private static final int GRID_SIZE = 5;
private static final int CELL_SIZE = 40;
private static final int PADDING = 3;
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
shell.setLayout(new GridLayout(GRID_SIZE, true));
for (int i = 0; i < GRID_SIZE * GRID_SIZE; i++) {
createButton(shell, i+1);
}
shell.pack ();
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
private static void createButton(Shell shell, final int number) {
Composite c = new Composite(shell, SWT.NONE);
c.setLayoutData(new GridData(CELL_SIZE, CELL_SIZE));
Button button = new Button(c, SWT.NONE);
button.setText(Integer.toString(number));
button.setBounds(-PADDING, -PADDING, CELL_SIZE + 2 * PADDING, CELL_SIZE + 2 * PADDING);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.err.println("click on " + number);
}
});
}
It fulfills your requirement: focus, centered. The result may be more or less nice depending on the Windows version (try to change the padding, or add a border to the Composite, it may look nicer).

Categories

Resources