How can I make my ScrolledComposite scroll to show a specified control within it at the top?
This is a followup to a previous question of mine about how to make a ScrolledComposite programmatically scroll to a child control. The advice given there gave me some ideas on how to attempt it conceptually, such as using the setOrigin method, but I could not successfully get neither the given example nor my own to quite function. Here is what I've constructed so far:
I have made a shell that looks like this:
I have a bunch of labels from 1-500, and I have a bunch of specific buttons that I want to cause the ScrolledComposite to center on its respective label, like so:
Here is the code:
import java.util.HashMap;
public class ScrollableTest2 extends Shell {
private static final int NUM_OF_LABELS = 500;
private static final int[] SEEKABLES = new int[] {0, 9, 23, 99, 175, 176, 177, 178, 350, 495, 499};
Map<Integer, Control> controlMap = new HashMap<Integer, Control>();
private Composite cmpButtons;
private Label vr;
private ScrolledComposite scroller;
private Composite cmpScrollInner;
/**
* Launch the application.
* #param args
*/
public static void main(String args[]) {
try {
Display display = Display.getDefault();
ScrollableTest2 shell = new ScrollableTest2(display);
shell.layout();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Create the shell.
* #param display
*/
public ScrollableTest2(Display display) {
super(display, SWT.SHELL_TRIM);
createContents();
}
/**
* Create contents of the shell.
*/
protected void createContents() {
setSize(400, 400);
setText("SWT Application");
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
setLayout(gridLayout);
cmpButtons = new Composite(this, SWT.NONE);
cmpButtons.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
cmpButtons.setLayout(new GridLayout(1, false));
for (int seekable : SEEKABLES) {
Button button = new Button(cmpButtons, SWT.NONE);
button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
button.setText("Go To " + String.valueOf(seekable+1));
final int index = seekable;
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
seekToLabel(index);
}
});
}
vr = new Label(this, SWT.SEPARATOR | SWT.VERTICAL);
vr.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true, 1, 1));
scroller = new ScrolledComposite(this, SWT.BORDER | SWT.V_SCROLL);
scroller.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
scroller.setExpandHorizontal(true);
scroller.setExpandVertical(true);
cmpScrollInner = new Composite(scroller, SWT.NONE);
cmpScrollInner.setLayout(new GridLayout(1, false));
scroller.setContent(cmpScrollInner);
for (int i = 0; i < NUM_OF_LABELS; i++) {
Label label = new Label(cmpScrollInner, SWT.NONE);
label.setText("Label " + String.valueOf(i+1));
}
scroller.setMinSize(cmpScrollInner.computeSize(SWT.DEFAULT, SWT.DEFAULT));
scroller.setContent(cmpScrollInner);
scroller.addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent e) {
Rectangle r = scroller.getClientArea();
scroller.setMinSize(cmpScrollInner.computeSize(r.width,
SWT.DEFAULT));
}
});
}
#Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
protected void seekToLabel(int index) {
Control showCntrl = controlMap.get(new Integer(index));
if(showCntrl != null){
scroller.setOrigin(showCntrl.getLocation());
}
}
}
The buttons do not seem to do anything at all, even though I believe that I am setting up the listeners correctly. I have to assume, then, that I am misusing the setOrigin command. What am I missing?
Neeeeevermind! Turns out, I simply forgot to add the control to the Map of controls. Once I did that, it works perfectly.
for (int i = 0; i < NUM_OF_LABELS; i++) {
Label label = new Label(cmpScrollInner, SWT.NONE);
label.setText("Label " + String.valueOf(i+1));
controlMap.put(new Integer(i), label);
}
It was a stupid mistake after all.
Related
First of all, this is an added question
Previous question :
Color change by area in swt?
I solved this once, but afterwards I tried to draw several boxes on this canvas, but an error came out and contact me
error
Exception in thread "main" org.eclipse.swt.SWTException: Graphic is disposed
at org.eclipse.swt.SWT.error(SWT.java:4869)
at org.eclipse.swt.SWT.error(SWT.java:4784)
at org.eclipse.swt.SWT.error(SWT.java:4755)
at org.eclipse.swt.graphics.GC.setBackground(GC.java:4122)
at View.MainWindow$2$1.paintControl(MainWindow.java:137)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:234)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4195)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1037)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1046)
at org.eclipse.swt.widgets.Composite.WM_PAINT(Composite.java:1616)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4816)
at org.eclipse.swt.widgets.Canvas.windowProc(Canvas.java:340)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:4921)
at org.eclipse.swt.internal.win32.OS.DispatchMessage(Native Method)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3610)
at View.MainWindow.init(MainWindow.java:185)
at View.MainWindow.main(MainWindow.java:193)
my code
public class MainWindow implements Runnable {
/**
* Launch the application.
*
* #param args
*/
public void init() {
Display display = Display.getDefault();
Shell shell = new Shell();
shell.setSize(720, 500);
shell.setText("SWT Application");
shell.setLayout(null);
org.eclipse.swt.graphics.Color blue = display.getSystemColor(SWT.COLOR_BLUE);
org.eclipse.swt.graphics.Color green = display.getSystemColor(SWT.COLOR_GREEN);
org.eclipse.swt.graphics.Color darkBlue = display.getSystemColor(SWT.COLOR_DARK_BLUE);
org.eclipse.swt.graphics.Color red = display.getSystemColor(SWT.COLOR_RED);
org.eclipse.swt.graphics.Color gray = display.getSystemColor(SWT.COLOR_GRAY);
Canvas canvas = new Canvas(shell, SWT.NONE);
canvas.setBounds(41, 130, 640, 282);
Composite composite_1 = new Composite(canvas, SWT.NONE);
composite_1.setSize(1, 282);
Composite composite_2 = new Composite(canvas, SWT.NONE);
composite_2.setSize(640, 282);
composite_2.setBackground(blue);
Canvas canvas2 = new Canvas(composite_1, SWT.NONE);
canvas2.setBounds(0,0, 640, 282);
canvas2.setBackground(green);
Label lblNewLabel = new Label(composite_2, SWT.NONE);
lblNewLabel.setBounds(10, 69, 142, 156);
lblNewLabel.setImage(new Image(display, "C:\\image.png"));
Label degree0 = new Label(shell, SWT.NONE);
degree0.setBounds(0, 122, 35, 15);
degree0.setText(" 0 -");
Label degree360 = new Label(shell, SWT.NONE);
degree360.setBounds(0, 402, 35, 15);
degree360.setText("360 -");
Label startDistance = new Label(shell, SWT.NONE);
startDistance.setBounds(43, 423, 87, 15);
startDistance.setText("0 m");
Label previousDistance = new Label(shell, SWT.NONE);
previousDistance.setBounds(659, 423, 35, 15);
previousDistance.setText("30m");
/*ArrayList<Integer> compositeArray = new ArrayList<Integer>();
HashMap<Integer, Composite> myHashMap = new HashMap<Integer, Composite>();
for(int i=0; i<100; i++) { compositeArray.add(i); }
for(int i=0; i<=100; i++) {
myHashMap.put(i, Composite "$tempComposite"+compositeArray.get(i));
myHashMap.get(i).setBounds(0, 0, 0, 0);
}
myHashMap.forEach((k,v)->{
System.out.println("key: "+k+"value: "+v);
});
*/
//canvas.setBounds(41, 130, 640, 282);
canvas2.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setForeground(red);
e.gc.setBackground(red);
e.gc.drawRectangle(0, 0, 5, 10);
e.gc.dispose();
}
});
final Runnable run = new Runnable() {
private int counter = 0;
#Override
public void run() {
//Image Move
lblNewLabel.setLocation(lblNewLabel.getLocation().x + 1, lblNewLabel.getLocation().y);
//Composite Background GreenColor
composite_1.setSize(composite_1.getSize().x + 1, 282);
composite_1.setBackground(green);
// ramdom 함수
int random = (int) (Math.random() * 10);
int randomH = (int) (Math.random() * 30);
int randomW = (int) (Math.random() * 10) + 1;
// TODO : 그냥 사각형 을 생성 및 점 생성 해야될거 같음
// 0~2 중만 점찍기
switch (random) {
case 0: {
//canvas2.redraw();
canvas2.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setBackground(red);
e.gc.drawRectangle(composite_1.getSize().x, composite_1.getSize().y, 5, 15);
canvas2.dispose();
e.gc.dispose();
}
});
System.out.print(random);
break;
}
case 1: {
//canvas2.redraw();
canvas2.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setBackground(gray);
e.gc.drawRectangle(composite_1.getSize().x, composite_1.getSize().y, 5, 15);
canvas2.dispose();
e.gc.dispose();
}
});
System.out.print(random);
break;
}
case 2: {
//canvas2.redraw();
canvas2.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setBackground(darkBlue);
e.gc.drawRectangle(composite_1.getSize().x, composite_1.getSize().y, 5, 15);
canvas2.dispose();
e.gc.dispose();
}
});
System.out.print(random);
break;
}
}
display.timerExec(1000, this);
}
};
display.timerExec(1000, run);
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
public static void main(String[] args) {
MainWindow a = new MainWindow();
a.init();
}
public void redrawCanvas (Canvas canvas) {
GC gc = new GC(canvas);
gc.dispose();
}
#Override
public void run() {
// TODO Auto-generated method stub
Display.getDefault().timerExec(1000, this);
}
}
I also dispose of the GC, but I don't know why this error appears. Can you tell me where I was wrong?
Really swt is difficult so i need help
Remove all the
e.gc.dispose();
calls.
You should only call dispose on GC objects that you create with new GC(...), GC objects that SWT gives you must not be disposed.
You must also remove all the canvas2.dispose() calls. You must not dispose of a control until it will never be needed again.
I'm generating a dialog with a lot of content. I want to pack the dialog to the childrens height, and I want to center it on screen, so I'm doing this after adding all the childrens:
shell.open();
shell.layout();
shell.pack(); //for wrap the dialog size to it's content width and height.
//the dialog must be centered after doing shell.pack();
Rectangle parentSize = getParent().getBounds();
Rectangle shellSize = shell.getBounds();
int x = parentSize.x + (parentSize.width - shellSize.width) / 2;
int y = (int) (parentSize.y + (parentSize.height - shellSize.height) / 3.5);
shell.setLocation(new Point(x, y));
The problem is that for some milliseconds the dialog is being visible with a lot of width and on the top left corner of the screen. I tried doing setVisible(false) and (true) to shell to do the trick but it doesn't works.
How can I avoid to see the dialog on the top left corner and with a lot of width for some milliseconds?
This is the full code:
public class SelectTwoMatrixNumbersDialog extends Dialog {
protected ArrayList<Integer> matrixNumbers1;
protected ArrayList<Integer> matrixNumbers2;
protected Shell shell;
private JointBet jointBet;
private boolean result;
NumbersMatrixWidget numbersMatrixWidget1;
NumbersMatrixWidget numbersMatrixWidget2;
/**
* Create the dialog.
* #param parent
* #param style
*/
public SelectTwoMatrixNumbersDialog(Shell parent, int style, JointBet jointBet) {
super(parent, style);
setText("Elegir Números");
this.jointBet = jointBet;
}
/**
* Open the dialog.
* #return the result
*/
public boolean open() {
createContents();
matrixNumbers1 = new ArrayList<Integer>();
matrixNumbers2 = new ArrayList<Integer>();
GridLayout gl_shell = new GridLayout(1, false);
gl_shell.marginWidth = 20;
gl_shell.marginHeight = 20;
shell.setLayout(gl_shell);
Composite contentComposite = new Composite(shell, SWT.NONE);
contentComposite.setLayout(new GridLayout(1, false));
contentComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 1));
Label lblNumbers = new Label(contentComposite, SWT.NONE);
lblNumbers.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
lblNumbers.setText("Números:");
Composite numbersContainerComposite = new Composite(contentComposite, SWT.NONE);
numbersContainerComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
//variable values depending of type of game
boolean firstCellEmpty = jointBet.getGameType().getNumbersMatrixArray()[0].isFirstCellEmpty();
int matrix1Rows = jointBet.getGameType().getNumbersMatrixArray()[0].getRows();
int matrix1Columns = jointBet.getGameType().getNumbersMatrixArray()[0].getColumns();
int matrix1MinNumber = jointBet.getGameType().getNumbersMatrixArray()[0].getMinNumber();
int matrix1MaxNumber = jointBet.getGameType().getNumbersMatrixArray()[0].getMaxNumber();
GridLayout gl_numbersContainerComposite = new GridLayout(2, false);
gl_numbersContainerComposite.horizontalSpacing = 15;
numbersContainerComposite.setLayout(gl_numbersContainerComposite);
numbersMatrixWidget1 = new NumbersMatrixWidget(numbersContainerComposite, SWT.BORDER, false, jointBet.getGameType().getNumbersMatrixArray()[0].getNumbers(), matrix1Rows, matrix1Columns, matrix1MinNumber, matrix1MaxNumber, firstCellEmpty);
//variable values depending of type of game
firstCellEmpty = jointBet.getGameType().getNumbersMatrixArray()[1].isFirstCellEmpty();
int matrix2Rows = jointBet.getGameType().getNumbersMatrixArray()[1].getRows();
int matrix2Columns = jointBet.getGameType().getNumbersMatrixArray()[1].getColumns();
int matrix2MinNumber = jointBet.getGameType().getNumbersMatrixArray()[1].getMinNumber();
int matrix2MaxNumber = jointBet.getGameType().getNumbersMatrixArray()[1].getMaxNumber();
numbersMatrixWidget2 = new NumbersMatrixWidget(numbersContainerComposite, SWT.BORDER, false, jointBet.getGameType().getNumbersMatrixArray()[1].getNumbers(), matrix2Rows, matrix2Columns, matrix2MinNumber, matrix2MaxNumber, firstCellEmpty);
numbersMatrixWidget2.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false, 1, 1));
Composite fillNumbersComposite = new Composite(shell, SWT.NONE);
fillNumbersComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
fillNumbersComposite.setLayout(new GridLayout(1, false));
Composite compositeFillMatrix1 = new Composite(fillNumbersComposite, SWT.NONE);
compositeFillMatrix1.setLayout(new GridLayout(4, false));
Label lblFillMatrix1WithMostCommonNumbers1 = new Label(compositeFillMatrix1, SWT.NONE);
lblFillMatrix1WithMostCommonNumbers1.setText("Rellenar Matriz 1 con los");
Spinner matrix1NumbersSpinner = new Spinner(compositeFillMatrix1, SWT.BORDER);
matrix1NumbersSpinner.setMaximum(jointBet.getGameType().getNumbersMatrixArray()[0].getPossibleNumbersCount());
matrix1NumbersSpinner.setMinimum(1);
Label lblFillMatrix1WithMostCommonNumbers2 = new Label(compositeFillMatrix1, SWT.NONE);
lblFillMatrix1WithMostCommonNumbers2.setText("números mas repetidos:");
Button btnFillMatrix1 = new Button(compositeFillMatrix1, SWT.NONE);
btnFillMatrix1.setText("Rellenar");
btnFillMatrix1.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
String gameType = jointBet.getGameType().getType();
int matrix = 1;
int amount = Integer.parseInt(matrix1NumbersSpinner.getText());
numbersMatrixWidget1.setNumbers(StatisticsManager.getInstance().getMostUsedNumberKeys(gameType, matrix, amount));
}
});
Composite compositeFillMatrix2 = new Composite(fillNumbersComposite, SWT.NONE);
compositeFillMatrix2.setLayout(new GridLayout(4, false));
Label lblFillMatrix2WithMostCommonNumbers1 = new Label(compositeFillMatrix2, SWT.NONE);
lblFillMatrix2WithMostCommonNumbers1.setText("Rellenar Matriz 2 con los");
Spinner matrix2NumbersSpinner = new Spinner(compositeFillMatrix2, SWT.BORDER);
matrix2NumbersSpinner.setMaximum(jointBet.getGameType().getNumbersMatrixArray()[1].getPossibleNumbersCount());
matrix2NumbersSpinner.setMinimum(1);
Label lblFillMatrix2WithMostCommonNumbers2 = new Label(compositeFillMatrix2, SWT.NONE);
lblFillMatrix2WithMostCommonNumbers2.setText("números mas repetidos:");
Button btnFillMatrix2 = new Button(compositeFillMatrix2, SWT.NONE);
btnFillMatrix2.setText("Rellenar");
btnFillMatrix2.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
String gameType = jointBet.getGameType().getType();
int matrix = 2;
int amount = Integer.parseInt(matrix2NumbersSpinner.getText());
numbersMatrixWidget2.setNumbers(StatisticsManager.getInstance().getMostUsedNumberKeys(gameType, matrix, amount));
}
});
Composite buttonsComposite = new Composite(shell, SWT.NONE);
FillLayout fl_buttonsComposite = new FillLayout(SWT.HORIZONTAL);
fl_buttonsComposite.marginHeight = 10;
fl_buttonsComposite.spacing = 40;
buttonsComposite.setLayout(fl_buttonsComposite);
GridData gd_buttonsComposite = new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1);
gd_buttonsComposite.heightHint = 45;
buttonsComposite.setLayoutData(gd_buttonsComposite);
Button acceptButton = new Button(buttonsComposite, SWT.NONE);
acceptButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
//matrix 1
matrixNumbers1.clear();
for (Button b : numbersMatrixWidget1.getButtons()) {
if (b.getSelection()) {
matrixNumbers1.add(Integer.parseInt(b.getText()));
}
}
//matrix 2
matrixNumbers2.clear();
for (Button b : numbersMatrixWidget2.getButtons()) {
if (b.getSelection()) {
matrixNumbers2.add(Integer.parseInt(b.getText()));
}
}
if (matrixNumbers1.size()<jointBet.getGameType().getNumbersMatrixArray()[0].getNumbersPerBet() || matrixNumbers2.size()<jointBet.getGameType().getNumbersMatrixArray()[1].getNumbersPerBet()) {
ErrorDialog dialog = new ErrorDialog(shell, SWT.DIALOG_TRIM | SWT.PRIMARY_MODAL, "El mínimo de números es "+jointBet.getGameType().getNumbersMatrixArray()[0].getNumbersPerBet()+" + "+jointBet.getGameType().getNumbersMatrixArray()[1].getNumbersPerBet());
dialog.open();
}else {
Collections.sort(matrixNumbers1);
Collections.sort(matrixNumbers2);
jointBet.setNumbers(0, matrixNumbers1);
jointBet.setNumbers(1, matrixNumbers2);
DataManager.getInstance().saveData();
result = true;
shell.close();
}
}
});
acceptButton.setBounds(0, 0, 75, 25);
acceptButton.setText("Aceptar");
Button cancelButton = new Button(buttonsComposite, SWT.NONE);
cancelButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
result = false;
shell.close();
}
});
cancelButton.setBounds(0, 0, 75, 25);
cancelButton.setText("Cancelar");
shell.setDefaultButton(acceptButton);
shell.open();
shell.layout();
shell.pack(); //for wrap the dialog size to it's content width and height.
//the dialog must be centered after doing shell.pack();
Rectangle parentSize = getParent().getBounds();
Rectangle shellSize = shell.getBounds();
int x = parentSize.x + (parentSize.width - shellSize.width) / 2;
int y = (int) (parentSize.y + (parentSize.height - shellSize.height) / 3.5);
shell.setLocation(new Point(x, y));
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return result;
}
/**
* Create contents of the dialog.
*/
private void createContents() {
shell = new Shell(getParent(), getStyle());
shell.setText(getText());
}
}
Just move the shell.open() to the end of the size calculations - after the shell.setLocation.
I have two elements in a horizontal RowLayout.
I am able to specify a (minimal) with for the second element (e.g. 200 px). Furthermore...
a) If the total width of the shell is too small, the second element wraps to a new line. That works fine with the RowLayout.
b) If the total with is "large", the second (=last) element should grab the excess horizontal space.
Is b) possible with the RowLayout? Or do I need to use a GridLayout and implement the wrapping on my own (e.g. use one or two columns in the grid layout, depending on the size of the elements)?
public class RowLayoutDemo {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
Text firstElement = new Text(shell, SWT.NONE);
firstElement.setText("firstElement");
Text secondElement = new Text(shell, SWT.NONE);
secondElement.setText("secondElement");
secondElement.setBackground(new Color(null, 200, 0, 0));
int minWidth = 200;
RowData rowData = new RowData();
rowData.width = minWidth;
secondElement.setLayoutData(rowData);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
The RowLayout does not support what you are looking for. I recommend using a GridLayout with a resize listener as you suggested or implement your own layout.
I first tried to write a custom layout which turned out to be be hard. The second strategy based on GridLayout was easier. Here is a first draft for a custom Composite that switches the number of columns of its GridLayout to have one or two columns, depending on the size of its children.
(I still have an issues with flickering when resizing my Sections.)
package org.treez.core.adaptable.composite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
/**
* A row composite for only two children. If the children are too large for a single line, the second child is wrapped
* on a second line. The second child always grabs excess horizontal space. This composite automatically sets the
* LayoutData of its children. Therefore the LayoutData of the children should not be set manually.
*/
public class GrabbingRowComposite extends Composite {
//#region ATTRIBUTES
private int widthHintForFirstChild = 80;
private int minWidthForSecondChild = 200;
private boolean isSingleLine = true;
//#end region
//#region CONSTRUCTORS
public GrabbingRowComposite(Composite parent) {
super(parent);
super.setLayout(createLayout());
if (parent.getLayout() instanceof GridLayout) {
GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
setLayoutData(gridData);
}
}
//#end region
//#region METHODS
private GridLayout createLayout() {
//create default layout for single line
GridLayout gridLayout = new GridLayout(2, false);
//change layout to use several lines if required
this.addControlListener(new ControlAdapter() {
#Override
public void controlResized(ControlEvent e) {
adaptLayout();
}
});
return gridLayout;
}
private void adaptLayout() {
int totalWidth = this.getBounds().width;
Control[] children = getChildren();
if (children.length < 2) {
return;
}
Control firstChild = children[0];
Control secondChild = children[1];
int firstChildWidth = children[0].getBounds().width;
setWidthHintForFirstChild(firstChild);
setGrapHorizontalSpaceForSecondChild(secondChild);
boolean isTooSmallForOneLine = totalWidth < firstChildWidth + minWidthForSecondChild;
if (isTooSmallForOneLine) {
setTwoLineLayout();
} else {
setSingleLineLayout();
}
}
private void setTwoLineLayout() {
if (isSingleLine) {
super.setLayout(new GridLayout(1, false));
updateLayoutOfParentAndGrandParent();
isSingleLine = false;
}
}
private void setSingleLineLayout() {
if (!isSingleLine) {
super.setLayout(new GridLayout(2, false));
updateLayoutOfParentAndGrandParent();
isSingleLine = true;
}
}
private void setWidthHintForFirstChild(Control firstChild) {
GridData firstGridData = new GridData();
firstGridData.widthHint = widthHintForFirstChild;
firstChild.setLayoutData(firstGridData);
}
private static void setGrapHorizontalSpaceForSecondChild(Control secondChild) {
GridData secondGridData = new GridData(SWT.FILL, SWT.FILL, true, false);
secondChild.setLayoutData(secondGridData);
}
private void updateLayoutOfParentAndGrandParent() {
Composite parent = getParent();
if (parent != null) {
parent.layout(true);
Composite grandParent = parent.getParent();
if (grandParent != null) {
grandParent.layout(true);
}
}
}
//#end region
//#region ACCESSORS
#Override
public void setLayout(Layout layout) {
throw new IllegalStateException("The layout of this composite must not be changed");
}
//#end region
}
Example usage
package org.treez.core.adaptable.composite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Section;
public class GrabbingRowCompositeDemo {
public static void main(String[] args) {
Shell shell = createShell();
shell.setSize(500, 300);
Section section = createSection(shell);
Composite parentComposite = createParentComposite(section);
createRow(parentComposite, "first");
createRow(parentComposite, "second");
createRow(parentComposite, "third");
section.clientVerticalSpacing = 0;
showUntilClosed(shell);
}
private static Shell createShell() {
Display display = new Display();
Shell shell = new Shell(display);
GridLayout shellGridLayout = new GridLayout(1, false);
shell.setLayout(shellGridLayout);
return shell;
}
private static Section createSection(Shell shell) {
Section section = new Section(
shell,
ExpandableComposite.TWISTIE | //
ExpandableComposite.EXPANDED | //
ExpandableComposite.TITLE_BAR | //
ExpandableComposite.CLIENT_INDENT);
GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
section.setLayoutData(gridData);
return section;
}
private static Composite createParentComposite(Section section) {
Composite parentComposite = new Composite(section, SWT.NONE);
section.setClient(parentComposite);
parentComposite.setBackground(new Color(null, 0, 0, 255));
GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
parentComposite.setLayoutData(gridData);
GridLayout gridLayout = new GridLayout(1, false);
parentComposite.setLayout(gridLayout);
return parentComposite;
}
private static Composite createRow(Composite parent, String text) {
GrabbingRowComposite row = new GrabbingRowComposite(parent);
row.setBackground(new Color(null, 255, 255, 255));
Label label = new Label(row, SWT.NONE);
label.setText(text);
Button checkBox = new Button(row, SWT.CHECK);
checkBox.setBackground(new Color(null, 255, 0, 0));
return row;
}
private static void showUntilClosed(Shell shell) {
shell.open();
Display display = Display.getCurrent();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
I am working on a wizard, which should have an error composite showed on the top of controls if an error occurs. This composite contains a Text listing all errors and two Labels: above and below this component. The Label below the error list can be omitted. Dependent on the amount of errors the height of the error list should vary. However it should not exceed the height of the parent composite. If the errors don't fit into the Text with the full height, a scroll bar should be shown.
Here's a minimized snippet of what I reached till now:
package de.dannylo.issues;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
public class TestWizardPage
extends WizardPage
{
private static final String PLUGIN_ID = "de.dannylo.issues.TestPlugin";
private Composite mainComposite;
private Composite stackComposite;
private StackLayout stackLayout;
private Composite defaultMessageComposite;
private Text idText;
private static Wizard wizard;
public TestWizardPage()
{
super("portalAppWizardMavenPage");
setTitle("TestWizard");
setDescription("Testing...");
}
#Override
public void createControl(Composite parent)
{
mainComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
mainComposite.setLayout(layout);
layout.numColumns = 1;
layout.verticalSpacing = 15;
// source folder
Composite idComposite = new Composite(mainComposite, SWT.NONE);
idComposite.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
idComposite.setLayout(new GridLayout(2, false));
Label label = new Label(idComposite, SWT.NONE);
label.setText("ID:");
idText = new Text(idComposite, SWT.BORDER | SWT.SINGLE);
idText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
label = new Label(mainComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
stackComposite = new Composite(mainComposite, SWT.NONE);
GridData layoutData = new GridData(SWT.FILL, SWT.NONE, true, false);
layoutData.heightHint = 250; // For testing purposes in the real GUI
// there are several components filling
// this composite
stackComposite.setLayoutData(layoutData);
stackLayout = new StackLayout();
stackComposite.setLayout(stackLayout);
defaultMessageComposite = new Composite(stackComposite, SWT.NONE);
defaultMessageComposite.setLayout(new GridLayout());
stackLayout.topControl = defaultMessageComposite;
label = new Label(defaultMessageComposite, SWT.NONE);
label.setText("Enter \"1\", \"2\" or \"3\" into the text field above.");
idText.addModifyListener(new ModifyListener()
{
#Override
public void modifyText(ModifyEvent e)
{
if ("1".equals(idText.getText()))
showErrorComposite("You entered id 1", "Nice job!", createTestStatusList(4));
else if ("2".equals(idText.getText()))
showErrorComposite("You entered id 2", "Oops", createTestStatusList(20));
else if ("3".equals(idText.getText()))
showErrorComposite("You entered id 3", null, createTestStatusList(20));
else
{
stackLayout.topControl = defaultMessageComposite;
stackComposite.layout();
}
}
});
setControl(mainComposite);
}
private static List<IStatus> createTestStatusList(int amount)
{
List<IStatus> toRet = new ArrayList<IStatus>();
for (int i = 0; i < amount; i++)
{
toRet.add(new Status(IStatus.ERROR, PLUGIN_ID, "Error message " + i + ": " + toRet.hashCode()));
}
return toRet;
}
private void showErrorComposite(String topMessage, String bottomMessage, List<IStatus> statusList)
{
final Composite errorComposite = new Composite(stackComposite, SWT.BORDER);
final GridLayout layout = new GridLayout();
layout.verticalSpacing = 20;
errorComposite.setLayout(layout);
errorComposite.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
final Label topLabel = new Label(errorComposite, SWT.WRAP | SWT.BORDER);
topLabel.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
topLabel.setText(topMessage);
final Text errorList = new Text(errorComposite, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY | SWT.V_SCROLL
| SWT.BORDER);
final GridData gd_errorList = new GridData(SWT.FILL, SWT.NONE, true, false);
gd_errorList.horizontalIndent = 20;
errorList.setLayoutData(gd_errorList);
for (int i = 0; i < statusList.size(); i++)
{
IStatus status = statusList.get(i);
errorList.append("\u2022 " + status.getMessage());
if (i != statusList.size() - 1)
errorList.append("\n\n");
}
final Label bottomLabel;
if (bottomMessage != null)
{
bottomLabel = new Label(errorComposite, SWT.WRAP | SWT.BORDER);
bottomLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
bottomLabel.setText(bottomMessage);
}
else
bottomLabel = null;
final Listener scrollBarListener = new Listener()
{
#Override
public void handleEvent(Event event)
{
errorList.removeListener(SWT.Resize, this);
int marginHeight = ((GridLayout)errorComposite.getLayout()).marginHeight;
int stackCompositeHeight = stackComposite.getClientArea().height;
int topLabelHeight = topLabel.getSize().y;
int verticalSpacing = layout.verticalSpacing;
int bottomLabelHeight = bottomLabel == null ? 0 : bottomLabel.getSize().y;
int spaceAboveErrorList = marginHeight + topLabelHeight + verticalSpacing;
int spaceBelowErrorList = bottomLabel == null ? marginHeight + 15 : verticalSpacing
+ bottomLabelHeight
+ marginHeight + 15;
int hHint = stackCompositeHeight - spaceAboveErrorList - spaceBelowErrorList;
Rectangle errorListClientArea = errorList.getClientArea();
Point errorListSize = errorList.computeSize(errorListClientArea.x, SWT.DEFAULT, true);
if (stackCompositeHeight < spaceAboveErrorList + errorListSize.y + spaceBelowErrorList)
{
gd_errorList.heightHint = hHint;
errorList.getVerticalBar().setVisible(true);
}
else
{
gd_errorList.heightHint = errorListSize.y;
errorList.getVerticalBar().setVisible(false);
}
errorComposite.layout();
errorList.addListener(SWT.Resize, this);
}
};
errorList.addListener(SWT.Resize, scrollBarListener);
stackLayout.topControl = errorComposite;
stackComposite.layout();
}
public static void main(String[] args)
{
Display.getDefault().syncExec(new Runnable()
{
#Override
public void run()
{
wizard = new Wizard()
{
#Override
public void addPages()
{
addPage(new TestWizardPage());
}
#Override
public boolean performFinish()
{
MessageDialog.openInformation(getShell(), "Bye!", "Thanks for testing!");
return true;
}
};
}
});
new WizardDialog(wizard.getShell(), wizard).open();
}
}
I have a problem with the ID2. In this case the height of the Text is maximum and the bottomLabel should be shown. Unfortunately this label doesn't appear as it's composite appears, but rather it appears only after the width of the window changes. It seems to be some problem with layouting. I already tried to use errorComposite.layout(true, true) (and even getShell().layout(true, true)) to flush the cache and redraw the children but this didn't help. Any ideas on how to fix that issue?
I solved your issue by entirely removing the scrollBarListener and fixing your layout a bit.
The showErrorComposite(...) method now looks like this:
private void showErrorComposite(String topMessage, String bottomMessage, List<IStatus> statusList)
{
final Composite errorComposite = new Composite(stackComposite, SWT.BORDER);
final GridLayout layout = new GridLayout();
layout.verticalSpacing = 20;
errorComposite.setLayout(layout);
errorComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Label topLabel = new Label(errorComposite, SWT.WRAP | SWT.BORDER);
topLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
topLabel.setText(topMessage);
final Text errorList = new Text(errorComposite, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY | SWT.V_SCROLL
| SWT.BORDER);
final GridData gd_errorList = new GridData(SWT.FILL, SWT.FILL, true, true);
gd_errorList.horizontalIndent = 20;
errorList.setLayoutData(gd_errorList);
for (int i = 0; i < statusList.size(); i++)
{
IStatus status = statusList.get(i);
errorList.append("\u2022 " + status.getMessage());
if (i != statusList.size() - 1)
errorList.append("\n\n");
}
errorList.setTopIndex(0);
final Label bottomLabel;
if (bottomMessage != null)
{
bottomLabel = new Label(errorComposite, SWT.WRAP | SWT.BORDER);
bottomLabel.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
bottomLabel.setText(bottomMessage);
}
else
bottomLabel = null;
stackLayout.topControl = errorComposite;
stackComposite.layout();
}
It now looks like this:
UPDATE
Ok, in your particular case, leave your code as it is and just add this line at the end of your showErrorComposite(...) method:
scrollBarListener.handleEvent(null);
I'm adding column one by one to my swt table. with
int totalwidth=0;
for(String s:phoneErrs){
TableColumn tblclmnError = new TableColumn(tablephone, SWT.CENTER);
tblclmnError.setText(s);
tblclmnError.pack();
totalwidth+=tblclmnError.getWidth();
}
and after this I want to add a last column, that should fill the rest of the
space in table header. Now that I have the total width of the added columns
already, I should be able to calculate the width of my last column and specify
it right? but how? I tried
TableColumn tblclmnComment = new TableColumn(tablephone, SWT.CENTER);
tblclmnComment.setWidth(tablephone.getSize().x-totalwidth);
tblclmnComment.setText("Comment");
but it's not working. the getSize() return 0.
You can achieve this by adding listener on SWT.Resize event type.
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, true));
TableViewer viewer1 = getViewer(shell);
List<String> rows = new ArrayList<String>();
rows.add("Row 1");
rows.add("Row 2");
viewer1.setInput(rows);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private static TableViewer getViewer(final Shell shell) {
TableViewer viewer = new TableViewer(shell, SWT.FULL_SELECTION
| SWT.H_SCROLL | SWT.V_SCROLL | SWT.NONE);
viewer.getTable().addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(Event event) {
Table table = (Table)event.widget;
int columnCount = table.getColumnCount();
if(columnCount == 0)
return;
Rectangle area = table.getClientArea();
int totalAreaWdith = area.width;
int lineWidth = table.getGridLineWidth();
int totalGridLineWidth = (columnCount-1)*lineWidth;
int totalColumnWidth = 0;
for(TableColumn column: table.getColumns())
{
totalColumnWidth = totalColumnWidth+column.getWidth();
}
int diff = totalAreaWdith-(totalColumnWidth+totalGridLineWidth);
TableColumn lastCol = table.getColumns()[columnCount-1];
//check diff is valid or not. setting negetive width doesnt make sense.
lastCol.setWidth(diff+lastCol.getWidth());
}
});
viewer.setContentProvider(ArrayContentProvider.getInstance());
viewer.getTable().setLayoutData(
new GridData(SWT.FILL, SWT.FILL, true, true));
TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
col.getColumn().setWidth(100);
col.getColumn().setText("Text Column");
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public void update(ViewerCell cell) {
cell.setText((String) cell.getElement());
}
});
col = new TableViewerColumn(viewer, SWT.NONE);
col.getColumn().setWidth(100);
col.getColumn().setText("Second Text Column");
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public void update(ViewerCell cell) {
cell.setText((String) cell.getElement());
}
});
viewer.getTable().setHeaderVisible(true);
return viewer;
}