SWT RowLayout with last element grabbing excess horizontal space? - java

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

Related

How to add a checkbox in a combo(Drop down) in java swt?

I want to have checkboxes in a combo(Drop down) so that I can select more than one elements of combo at once. Can this be done? If yes, can you please explain or provide any link if possible.
Thank you.
Original poster asked for a drop down list with multi-selection support as an alternative. Custom implementation of MultiSelectionCombo can serve this purpose.
Selected items are displayed in text field:
Drop down with multi-selection support http://s12.postimg.org/7ee1y6j5n/Multi_Selection_Combo.png
Keep Ctrl pressed to select multiple items:
Select multiple items in drop down http://s3.postimg.org/ufkezh99t/Items_selected.png
Get indices of selected items:
Get selected items http://s11.postimg.org/8n9fa7fb5/Get_selected_items.png
Implementation and demo of MultiSelectionCombo:
import java.util.Arrays;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class MultiSelectionCombo extends Composite {
Shell shell = null;
List list = null;
Text txtCurrentSelection = null;
String[] textItems = null;
int[] currentSelection = null;
public MultiSelectionCombo(Composite parent, String[] items, int[] selection, int style) {
super(parent, style);
currentSelection = selection;
textItems = items;
init();
}
private void init() {
GridLayout layout = new GridLayout();
layout.marginBottom = 0;
layout.marginTop = 0;
layout.marginLeft = 0;
layout.marginRight = 0;
layout.marginWidth = 0;
layout.marginHeight = 0;
setLayout(new GridLayout());
txtCurrentSelection = new Text(this, SWT.BORDER | SWT.READ_ONLY);
txtCurrentSelection.setLayoutData(new GridData(GridData.FILL_BOTH));
displayText();
txtCurrentSelection.addMouseListener(new MouseAdapter() {
#Override
public void mouseDown(MouseEvent event) {
super.mouseDown(event);
initFloatShell();
}
});
}
private void initFloatShell() {
Point p = txtCurrentSelection.getParent().toDisplay(txtCurrentSelection.getLocation());
Point size = txtCurrentSelection.getSize();
Rectangle shellRect = new Rectangle(p.x, p.y + size.y, size.x, 0);
shell = new Shell(MultiSelectionCombo.this.getShell(), SWT.NO_TRIM);
GridLayout gl = new GridLayout();
gl.marginBottom = 2;
gl.marginTop = 2;
gl.marginRight = 2;
gl.marginLeft = 2;
gl.marginWidth = 0;
gl.marginHeight = 0;
shell.setLayout(gl);
list = new List(shell, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
for (String value: textItems) {
list.add(value);
}
list.setSelection(currentSelection);
GridData gd = new GridData(GridData.FILL_BOTH);
list.setLayoutData(gd);
shell.setSize(shellRect.width, 100);
shell.setLocation(shellRect.x, shellRect.y);
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseUp(MouseEvent event) {
super.mouseUp(event);
currentSelection = list.getSelectionIndices();
if ((event.stateMask & SWT.CTRL) == 0) {
shell.dispose();
displayText();
}
}
});
shell.addShellListener(new ShellAdapter() {
public void shellDeactivated(ShellEvent arg0) {
if (shell != null && !shell.isDisposed()) {
currentSelection = list.getSelectionIndices();
displayText();
shell.dispose();
}
}
});
shell.open();
}
private void displayText() {
if (currentSelection != null && currentSelection.length > 0) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < currentSelection.length; i++) {
if (i > 0)
sb.append(", ");
sb.append(textItems[currentSelection[i]]);
}
txtCurrentSelection.setText(sb.toString());
}
else {
txtCurrentSelection.setText("");
}
}
public int[] getSelections() {
return this.currentSelection;
}
// Main method to showcase MultiSelectionCombo
// (can be removed from productive code)
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.setText("MultiSelectionCombo Demo");
// Items and pre-selected items in combo box
String[] items = new String[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa" };
int[] selection = new int[] { 0, 2 };
// Create MultiSelectCombo box
final MultiSelectionCombo combo = new MultiSelectionCombo(shell, items, selection, SWT.NONE);
combo.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
((GridData)combo.getLayoutData()).widthHint = 300;
// Add button to print current selection on console
Button button = new Button(shell, SWT.NONE);
button.setText("What is selected?");
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
System.out.println("Selected items: " + Arrays.toString(combo.getSelections()));
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
screenshot
https://github.com/lawhcd/SWTMultiCheckSelectionCombo
For anyone who is looking for a widget that allows user to select multiple options from a list of check box style options.
It is based on user1438038's idea and extended to provide nearly all of the api required of a widget similar to Combo.
This isn't possible with the SWT Combo (or CCombo).
The Eclipse Nebula project TableCombo supports a Table shown as a Combo, so you may be able to use an SWT.CHECK style table with this.
There is no such control in SWT on default. However, you can extend either Combo or CCombo to implement checkboxes yourself.
A Table with SWT.CHECK style might be a better option, though:
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
public class TableCheckBoxCell {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Table table = new Table(shell, SWT.CHECK | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
for (int i = 0; i < 12; i++) {
TableItem item = new TableItem(table, SWT.NONE);
item.setText("Item " + i);
}
table.setSize(100, 100);
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
Source: Table With CheckBox Cell

StackLayout in WizardDialog and varying multiline Text with fixed height and dynamically showed scrollbar

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

Implementing Polyline Connections between Ellipses

I'm trying to create a connection between two ellipses on a canvas, AFTER having created the ellipses and storing their positions on the canvas. These stored positions need to be used to create the connection. The code I've written works for RectangleFigures but does not for Ellipses. I can't seem to see the difference between the two cases. Could someone please help? Thanks.*I've added short testable code to illustrate my problem. To see it working with Rectangles, please uncomment the line saying UNCOMMENT THIS
import java.util.ArrayList;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.ChopboxAnchor;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.PolygonDecoration;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Label;
public class EllipseProblem
{
private Shell shell;
private Display display;
private final Label lblUnicorn;
private final Canvas canvas;
final IFigure panel;
public static ArrayList canvasElements= new ArrayList();
public static void main(String args[])
{
new EllipseProblem();
}
public EllipseProblem()
{
display = new Display();
shell = new Shell(display);
shell.setText("SWT Application");
shell.setLayout(new GridLayout(2, false));
Group grpPalette = new Group(shell, SWT.NONE);
grpPalette.setText("Palette");
grpPalette.setLayout(new GridLayout());
grpPalette.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, true));
lblUnicorn = new Label(grpPalette, SWT.BORDER | SWT.HORIZONTAL | SWT.CENTER);
lblUnicorn.setText("UNICORN");
lblUnicorn.setAlignment(SWT.CENTER);
final Group grpCanvas = new Group(shell, SWT.NONE);
grpCanvas.setText("Canvas");
grpCanvas.setLayout(new GridLayout());
grpCanvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
canvas = new Canvas(grpCanvas, SWT.NONE);
canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Button btnCreate = new Button(grpPalette, SWT.CENTER);
GridData gd_btnCreate = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gd_btnCreate.widthHint = 87;
gd_btnCreate.heightHint = 33;
btnCreate.setLayoutData(gd_btnCreate);
btnCreate.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
drawConnection();
}
});
btnCreate.setText("Make Connection");
LightweightSystem lws = new LightweightSystem(canvas);
panel = new Figure();
lws.setContents(panel);
DragSource dragSource1 = new DragSource(lblUnicorn, DND.DROP_COPY);
Transfer[] transfers1 = new Transfer[] { TextTransfer.getInstance() };
dragSource1.setTransfer(transfers1);
dragSource1.addDragListener(new DragSourceListener()
{
public void dragStart(DragSourceEvent event)
{
if (lblUnicorn.getText().length() == 0)
{
event.doit = false;
}
}
public void dragSetData(DragSourceEvent event)
{
if (TextTransfer.getInstance().isSupportedType(event.dataType))
{
event.data = lblUnicorn.getText();
}
}
public void dragFinished(DragSourceEvent event)
{
}
});
Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
DropTarget dropTarget = new DropTarget(canvas, DND.DROP_COPY | DND.DROP_DEFAULT);
dropTarget.setTransfer(types);
dropTarget.addDropListener(new DropTargetListener()
{
public void dragEnter(DropTargetEvent event)
{
if (event.detail == DND.DROP_DEFAULT)
{
if ((event.operations & DND.DROP_COPY) != 0)
{
event.detail = DND.DROP_COPY;
}
else
{
event.detail = DND.DROP_NONE;
}
}
}
public void dragLeave(DropTargetEvent event)
{
}
public void dragOperationChanged(DropTargetEvent event)
{
}
public void dragOver(DropTargetEvent event)
{
}
public void drop(DropTargetEvent event)
{
}
public void dropAccept(final DropTargetEvent event)
{
if (TextTransfer.getInstance().isSupportedType(event.currentDataType))
{
String d = (String) TextTransfer.getInstance().nativeToJava(event.currentDataType);
final String finald= d;
org.eclipse.swt.graphics.Point droppoint = canvas.toControl(event.x, event.y);
canvasElements.add(droppoint);
Rectangle rect=new Rectangle(droppoint.x, droppoint.y, 40, 20);
Rectangle rect2=new Rectangle(droppoint.x+20, droppoint.y, 100, 25);
Ellipse node= new Ellipse();
//RectangleFigure node= new RectangleFigure(); UNCOMMENT THIS
node.setBounds(rect);
System.out.println(node.getBounds());
node.setLocation(new Point(droppoint.x, droppoint.y));
node.setBackgroundColor(ColorConstants.red);
panel.add(node);
org.eclipse.draw2d.Label droppedName=
new org.eclipse.draw2d.Label(finald);
droppedName.setLocation(new Point(droppoint.x, droppoint.y)); //draw2d. point
droppedName.setBounds(rect2);
panel.add(node);
panel.add(droppedName);
canvas.redraw();
}
}
});
shell.pack();
shell.setSize(400, 300);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
}
protected void drawConnection()
{
org.eclipse.swt.graphics.Point swt_origin= (org.eclipse.swt.graphics.Point) canvasElements.get(0);
org.eclipse.draw2d.geometry.Point origin_point= new org.eclipse.draw2d.geometry.Point(swt_origin.x, swt_origin.y);
org.eclipse.swt.graphics.Point swt_destination= (org.eclipse.swt.graphics.Point) canvasElements.get(1);
org.eclipse.draw2d.geometry.Point destination_point= new org.eclipse.draw2d.geometry.Point(swt_destination.x, swt_destination.y);
IFigure origin = panel.findFigureAt(origin_point);
IFigure destination = panel.findFigureAt(destination_point);
PolylineConnection conn = new PolylineConnection();
conn.setSourceAnchor(new ChopboxAnchor(origin));
conn.setTargetAnchor(new ChopboxAnchor(destination));
conn.setTargetDecoration(new PolygonDecoration());
panel.add(conn);
}
}
Your problem is caused by the fact, that your method to determine the source and target IFigures is flawed.
You store the top left corner for the lookup. That works just fine for the RectangleFigure, since that rectangle actually contains the top left corner. The Ellipse, however, doesn't (which is why the source and target of the PolylineConnection is their parent).
Best illustrated with an image:
If you instead store the center of the figure you'll end up with this:

Last column gets oversized in jface

I'm quite new with Jface, and right now I'm facing a problem with the last column in a table. Take a look at the next screenshot:
As you can see, the last column gets oversized when the application is initiated, and I can't fix that, no matter what! Below you can see the View code:
package de.vogella.jface.tableviewer;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;
import de.vogella.jface.tableviewer.model.CenterImageLabelProvider;
import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;
public class View extends ViewPart {
public static final String ID = "de.vogella.jface.tableviewer.view";
private TableViewer viewer;
// static fields to hold the images
private static final Image CHECKED = Activator.getImageDescriptor(
"icons/checked.gif").createImage();
private static final Image UNCHECKED = Activator.getImageDescriptor(
"icons/unchecked.gif").createImage();
public void createPartControl(Composite parent) {
GridLayout layout = new GridLayout(2, false);
parent.setLayout(layout);
Label searchLabel = new Label(parent, SWT.NONE);
searchLabel.setText("Search: ");
final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
| GridData.HORIZONTAL_ALIGN_FILL));
createViewer(parent);
}
private void createViewer(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
createColumns(parent, viewer);
final Table table = viewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);
viewer.setContentProvider(new ArrayContentProvider());
// get the content for the viewer, setInput will call getElements in the
// contentProvider
viewer.setInput(ModelProvider.INSTANCE.getPersons());
// make the selection available to other views
getSite().setSelectionProvider(viewer);
// set the sorter for the table
// define layout for the viewer
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
viewer.getControl().setLayoutData(gridData);
}
public TableViewer getViewer() {
return viewer;
}
// create the columns for the table
private void createColumns(final Composite parent, final TableViewer viewer) {
String[] titles = { "First name", "Last name", "Gender", "Married", "Age" };
int[] bounds = { 100, 100, 100, 100, 100 };
// first column is for the first name
TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
Person p = (Person) element;
return p.getFirstName();
}
});
// second column is for the last name
col = createTableViewerColumn(titles[1], bounds[1], 1);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
Person p = (Person) element;
return p.getLastName();
}
});
// now the gender
col = createTableViewerColumn(titles[2], bounds[2], 2);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
Person p = (Person) element;
return p.getGender();
}
});
// now the status married
col = createTableViewerColumn(titles[3], bounds[3], 3);
col.setLabelProvider(new CenterImageLabelProvider() {
#Override
public Image getImage(Object element) {
if (((Person) element).isMarried()) {
return CHECKED;
}
else {
return UNCHECKED;
}
}
});
// now the age
col = createTableViewerColumn(titles[4], bounds[4], 4);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
Person p = (Person) element;
return p.getAge().toString();
}
});
}
private TableViewerColumn createTableViewerColumn(String title,
int bound, final int colNumber) {
final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
SWT.CENTER);
final TableColumn column = viewerColumn.getColumn();
column.setText(title);
column.setWidth(bound);
column.setResizable(true);
column.setMoveable(true);
return viewerColumn;
}
public void setFocus() {
viewer.getControl().setFocus();
}
}
How can I fix this?
Your layout on the viewer:
GridData gridData = new GridData();
gridData.verticalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
viewer.getControl().setLayoutData(gridData);
says that you want the viewer to use all the available horizontal space, so the viewer has had to resize the last column to make this happen.
Use
gridData.grabExcessHorizontalSpace = false;
gridData.horizontalAlignment = GridData.BEGINNING;
to not grab the extra space.
Alternatively to fill the space available leave the GridData alone and use TableLayout and ColumnWeightData, something like:
TableLayout tableLayout = new TableLayout();
table.setLayout(tableLayout);
...
... don't call column.setWidth replace with:
tableLayout.addColumnData(new ColumnWeightData(nnn));
nnn is a number you choose for each column and is the 'weight' used to calculate the width of the column.

Using setOrigin method of ScrolledComposite

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.

Categories