Compute the minimum size of a Composite - java

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 ();
}

Related

SWT: how to compute correct size of wrapped text

I have a Label component which can contain a very long text. Label support wrapping:
Label label = new Label( composite, SWT.WRAP | SWT.LEFT );
So when I decrease the width of the parent Composite - just resize it with mouse - the text is wrapped correctly, but the height of the parent Composite is not changed so some part of my Label get hidden.
I tried to add a resize listener to parent composite:
expandBar.addListener( SWT.Resize, new Listener() {
public void handleEvent( Event e ) {
Point p = label.computeSize( SWT.DEFAULT, SWT.DEFAULT ) // compute size of the text
int h = p.y; // ! THIS value doesn't change
// despite text in label became wrapped
// now set expandBar current item's height = h;
}
} );
Is there a way to compute the height of the component with regards to wrapping?
If no, are there SWT components/layouts which change its size dynamically if the child is getting wrapped?

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.

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).

SWT - How do you re-size/set size of an a Composite residing inside another composite?

//I have a composite within a scrolled composite.
final Composite c = new Composite(scrolledComposite, SWT.NONE); //1
c.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
c.setLayout(new GridLayout(1, false));
//I have a for loop that adds composites to composite c
for(int i = 0; i<100; i++){
Composite b = new Composite(c, SWT.BORDER);
b.setBackground(SWTResourceManager.getColor(SWT.COLOR_LIST_SELECTION));
b.setLayout(new GridLayout(2, false));
b.setBounds(112, 70, 268, 69);
//and then some controllers added to that inner composite
Label lblNewLabel_2 = new Label(b, SWT.NONE);
lblNewLabel_2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 4));
Image img = new Image(Display.getCurrent(), result.get(i).getPicturePath());
//.... more controllers.
// finally I end the loop and add the outer most composite to an scrolled composite.
} // end loop
scrolledComposite.setContent(c); //setter content!
scrolledComposite.setMinSize(c.computeSize(SWT.DEFAULT, SWT.DEFAULT));
This all works great, almost. My problem is that the inner composite wont respond to the setBounds method. No matter what I'm writing in it I cant get it to expand. I suspect this have something to do with the layouts.
Anyone got a clue?
Thanks in advance.
Petter
Layouts are the objects that set size and location of composite's children so you wouldn't have to call setBounds() for every one of them. Check out Understanding Layouts in SWT.
What is the purpose of Layout in SWT applications?
Consider a Resize listener in your outer panel to have it adjust your inner panel.
_outerPanel.addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
computePanelSize(); // computes the inner panel size
}
});

How to have text fields fill 100% horizontally

If I have a text field with SWT, how can I get the field to fill to 100% or some specified width.
For example, this text field will only reach for so much horizontally.
public class Tmp {
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
GridLayout gridLayout = new GridLayout ();
shell.setLayout (gridLayout);
Button button0 = new Button(shell, SWT.PUSH);
button0.setText ("button0");
Text text = new Text(shell, SWT.BORDER | SWT.FILL);
text.setText ("Text Field");
shell.setSize(500, 400);
//shell.pack();
shell.open();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ())
display.sleep ();
}
display.dispose ();
}
}
Make something like this:
Text text = new Text(shell, SWT.BORDER);
text.setText ("Text Field");
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER));
/: Since this is the accepted answer I remove the errors. Thx for correcting me.
Positioning of elements in a Component depends on the Layout object that you are using. In the sample provided, you are using a GridLayout. That means, that you need to provide a specific LayoutData object to indicate how you want your component displayed. In the case of GridLayout, the object is GridData.
To achieve what you want, you must create a GridData object that grabs all horizontal space and fills it:
// Fills available horizontal and vertical space, grabs horizontal space,grab
// does not grab vertical space
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
text.setLayoutData(gd);
Alternative ways include using a different LayoutManager, such as FormLayout. This layout uses a FormData object that also allows you to specify how the component will be placed on the screen.
You can also read this article on Layouts to understand how Layouts work.
As a side note, the constructor new GridData(int style) is marked as "not recommended" in the documentation. The explicit constructor shown in this example is preferred instead.

Categories

Resources