I have a Composite on which I'd like to track SWT.MouseEnter and SWT.MouseExit events. The Composite, however, has another Composite inside it which consumes the entirety of the region, due to a FillLayout.
Some sample code to illustrate what it is I'm doing:
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class EventListenerTest {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
// Set up the outer Composite
Composite outerComp = new Composite(shell, SWT.BORDER);
FillLayout fl = new FillLayout();
// fl.marginHeight = 15;
// fl.marginWidth = 15;
outerComp.setLayout(fl);
// Set up a nested Composite
Composite innerComp = new Composite(outerComp, SWT.BORDER);
// Set a listener on the outer Composite for a MouseEnter event
outerComp.addListener(SWT.MouseEnter, new Listener() {
#Override
public void handleEvent(Event e) {
System.out.println("Mouse ENTER event");
}
});
// Similarly, for a MouseExit event
outerComp.addListener(SWT.MouseExit, new Listener() {
#Override
public void handleEvent(Event e) {
Composite comp = (Composite) e.widget;
// Don't report if positioned over a child control
for (Control child : comp.getChildren()) {
if (!child.getBounds().contains(new Point(e.x, e.y)))
System.out.println("Mouse EXIT event");
}
}
});
outerComp.pack();
outerComp.layout();
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
return;
}
}
Unfortunately, due to the fact that innerComp fills the entirety of its parent, outerComp never does record the mouse enter/exit events unless I expose a little bit of its "area".
I'm able to expose a little bit of outerComp by creating some margins on its FillLayout (commented out lines 21-22), but this is really not ideal. For aesthetic reasons, I can't have huge margins on outerComp, and reducing the margin size to 1 doesn't consistently detect the mouse events if I'm moving my mouse over the composite quickly (I have to move my mouse very slowly over the 1px margin for it to trigger).
From a design perspective on my project, I'm not actually supposed to know what outerComp contains (or how deep the controls nest), so setting the event listeners on its children also isn't ideal.
Is there any way I can still track these mouse events on the outerComp if it has a FillLayout consuming all of its area?
What you can do is add a filter to the Display and in the handler, check if the Widget that is the source of the Event is a child of your Composite.
This should give you an idea of how to do it:
public static void main(String[] args)
{
Display display = new Display();
Shell shell = new Shell();
shell.setText("StackOverflow");
shell.setLayout(new GridLayout(2, true));
final Composite left = new Composite(shell, SWT.NONE);
Composite right = new Composite(shell, SWT.NONE);
left.setLayout(new GridLayout(3, true));
right.setLayout(new GridLayout(3, true));
for (int i = 0; i < 6; i++)
{
new Label(left, SWT.NONE).setText("label-" + i);
new Label(right, SWT.NONE).setText("label-" + i);
}
display.addFilter(SWT.MouseMove, new Listener()
{
#Override
public void handleEvent(Event e)
{
if (isChildOrSelf(e.widget, left))
System.out.println(e);
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
private static boolean isChildOrSelf(Widget child, Composite parent)
{
if(child == parent)
return true;
for (Control c : parent.getChildren())
{
if (c instanceof Composite)
{
boolean result = isChildOrSelf(child, (Composite)c);
if (result)
return true;
}
else if (c == child)
return true;
}
return false;
}
I Have a GridLayout filled with Composites in a random order. Now I'm sorting the Composites that are filling the GridLayout in a List/Collection and want to order them like the sort result from my List/Collection. I tried to do it by allocating them again to their parent so they are in the right order, but for some reason nothing happened. Then I tried to cache them in a Composite you don't see and then bring them back to the parent with the same method as in the first attempt. No change at all. Anyone has a pointer? I'm ordering by date, just in case /so want to know.
Thats how my grid looks like, now I want to order them like in my arrayList();
The methods you are looking for are Control#moveAbove(Control control) and Control#moveBelow(Control control) to reorder the items:
private static List<Label> labels = new ArrayList<Label>();
private static List<Color> colors = new ArrayList<Color>();
public static void main(String[] args)
{
Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("Stackoverflow");
shell.setLayout(new RowLayout(SWT.VERTICAL));
colors.add(display.getSystemColor(SWT.COLOR_BLUE));
colors.add(display.getSystemColor(SWT.COLOR_CYAN));
colors.add(display.getSystemColor(SWT.COLOR_GREEN));
colors.add(display.getSystemColor(SWT.COLOR_YELLOW));
colors.add(display.getSystemColor(SWT.COLOR_RED));
for (int i = 0; i < 5; i++)
{
Label label = new Label(shell, SWT.BORDER);
label.setText("Button " + i);
label.setBackground(colors.get(i));
labels.add(label);
}
Button sortButton = new Button(shell, SWT.TOGGLE);
sortButton.setText("Sort");
sortButton.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event e)
{
Button source = (Button) e.widget;
final boolean asc = source.getSelection();
Label oldFirst = labels.get(0);
Collections.sort(labels, new Comparator<Label>()
{
#Override
public int compare(Label o1, Label o2)
{
int result = o1.getText().compareTo(o2.getText());
if (asc)
result = -result;
return result;
}
});
Label label = labels.get(0);
label.moveAbove(oldFirst);
for (int i = 1; i < labels.size(); i++)
{
labels.get(i).moveBelow(labels.get(i - 1));
}
shell.layout();
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
After starting:
After pressing the button:
I found the solution. You have to call child.moveAbove(otherChild) or .moveBelow() When you are done with the reordering just call on the parent Composite parent.layout()
I would design TitleAreaDialog similar to those Eclipse but without sucess, below there is two screenshot, Eclipse form and my own form,
i have problem to get the top and bottom separator, if you notice,Eclipse form don't gave a margin and the workspace have margin.
in my own form my separator have a margin
Eclipse Form :
http://farm8.staticflickr.com/7437/9075688435_d5c49770fa_b.jpg
My own Form
http://farm4.staticflickr.com/3688/9077919366_8c25c40a79_b.jpg
here is the source code of my class
public class CompteDialog extends TitleAreaDialog {
private Text idCompteText;
private Text libelleCompteText;
private Text ribCompteText;
private Text agenceCompteText;
public CompteDialog(Shell parentShell) {
super(parentShell);
}
#Override
public void create() {
super.create();
// Set the title
setTitle("Fiche comptes");
// Set the message
setMessage("Saisissez les informations relatives au compte",
IMessageProvider.INFORMATION);
}
#Override
protected Control createDialogArea(Composite parent) {
setTitleImage(ResourceManager.getPluginImage(
"dz.iaityahia.cieptalcars.matresorerie", "icons/wallet.png"));
GridLayout layout = new GridLayout();
layout.numColumns = 2;
parent.setLayout(layout);
// Champs ID
Label idCompteLabel = new Label(parent, SWT.NONE);
idCompteLabel.setText("Compte ID");
GridData gridData = new GridData(GridData.BEGINNING,
GridData.BEGINNING, true, false, 1, 1);
idCompteText = new Text(parent, SWT.BORDER);
idCompteText.setLayoutData(gridData);
idCompteText.setTextLimit(20);
gridData = new GridData(GridData.FILL, GridData.BEGINNING, true, false,
1, 1);
// Champs libelle
Label libelleCompteLabel = new Label(parent, SWT.NONE);
libelleCompteLabel.setText("Libellé compte");
libelleCompteText = new Text(parent, SWT.BORDER);
libelleCompteText.setLayoutData(gridData);
// Champs Rib
Label ribCompteLabel = new Label(parent, SWT.NONE);
ribCompteLabel.setText("R.I.B");
gridData = new GridData(GridData.FILL, GridData.BEGINNING, true, false,
1, 1);
ribCompteText = new Text(parent, SWT.BORDER);
ribCompteText.setLayoutData(gridData);
gridData = new GridData(GridData.FILL, GridData.BEGINNING, true, false,
1, 1);
// Champs libelle
Label agenceCompteLabel = new Label(parent, SWT.NONE);
agenceCompteLabel.setText("Agence bancaire");
agenceCompteText = new Text(parent, SWT.BORDER);
agenceCompteText.setLayoutData(gridData);
// Create a bottom separator
Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
line.setLayoutData(new GridData(SWT.FILL, SWT.END, true, true, 2, 1));
return parent;
}
#Override
protected Control createButtonBar(Composite parent) {
final Composite buttonBar = new Composite(parent, SWT.NONE);
final GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.makeColumnsEqualWidth = false;
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
buttonBar.setLayout(layout);
GridData gridData = new GridData(GridData.FILL, GridData.END, true,
true, 2, 1);
buttonBar.setLayoutData(gridData);
/*
* org.eclipse.swt.graphics.Color magenta =
* Display.getDefault().getSystemColor(SWT.COLOR_MAGENTA);
* buttonBar.setBackground(magenta);
*/
// Create Add button
// Own method as we need to overview the SelectionAdapter
createOkButton(buttonBar, OK, "Add", true);
// Add a SelectionListener
// Create Cancel button
Button cancelButton = createButton(buttonBar, CANCEL, "Cancel", false);
// Add a SelectionListener
cancelButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setReturnCode(CANCEL);
close();
}
});
return buttonBar;
}
protected Button createOkButton(Composite parent, int id, String label,
boolean defaultButton) {
Button button = new Button(parent, SWT.PUSH);
button.setText(label);
button.setFont(JFaceResources.getDialogFont());
button.setData(new Integer(id));
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
if (isValidInput()) {
okPressed();
}
}
});
if (defaultButton) {
Shell shell = parent.getShell();
if (shell != null) {
shell.setDefaultButton(button);
}
}
setButtonLayoutData(button);
return button;
}
private boolean isValidInput() {
boolean valid = true;
if (idCompteText.getText().length() == 0) {
setErrorMessage("Veuillez saisir le compte ID");
valid = false;
}
if (libelleCompteText.getText().length() == 0) {
setErrorMessage("Veuillez saisir le libellé du compte");
valid = false;
}
return valid;
}
#Override
protected boolean isResizable() {
return true;
}
}
Could someone give me an idea how to get TiteAreaDialog similar to those of Eclipse ?
with my thank's
I finally solved the problem myself, after thoroughly looked at the code of the TitleAreaDilog class, I found that there was no way to do this directly.
with a little imagination I found the trick, it was necessary to create two nested Composites
the parent Composite without margin will contain the two separators and the child composite which have margin and will contain others controls.
as we you can see in the example below.
I hope this will help some beginners like me
package dz.iaityahia.cieptalcars.matresorerie.dialogs;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.wb.swt.ResourceManager;
public class CompteDialog extends TitleAreaDialog {
private Text idCompteText;
private Text libelleCompteText;
private Text ribCompteText;
private Text agenceCompteText;
public CompteDialog(Shell parentShell) {
super(parentShell);
setHelpAvailable(true);
// TODO Auto-generated constructor stub
}
#Override
public void create() {
super.create();
// Set the title
setTitle("Fiche comptes");
// Set the message
setMessage("Saisissez les informations relatives au compte",
IMessageProvider.INFORMATION);
setTitleImage(ResourceManager.getPluginImage("dz.iaityahia.cieptalcars.matresorerie", "icons/wallet.png"));
}
#Override
protected Control createDialogArea(Composite parent) {
// cr�ation du Layout du la boite de dialog
GridLayout parentLayout = new GridLayout();
parentLayout.numColumns=1;
parentLayout.marginWidth=0;
parentLayout.marginHeight=0;
parent.setLayout(parentLayout);
//Cr�ation du s�parateur sup�rieur
Label linetop = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
linetop.setLayoutData(new GridData (SWT.FILL,SWT.TOP,true,false));
//Cr�ation du composite contenu
Composite workArea = new Composite(parent, SWT.NONE);
workArea.setLayoutData(new GridData (SWT.FILL,SWT.FILL,true,true));
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.marginWidth=10;
layout.marginHeight=10;
workArea.setLayout(layout);
// Champs ID
Label idCompteLabel = new Label(workArea,SWT.NONE);
idCompteLabel.setText("Compte ID");
idCompteText = new Text(workArea, SWT.BORDER);
idCompteText.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, true,false,1, 1));
idCompteText.setTextLimit(20);
// Champs libelle
Label libelleCompteLabel = new Label(workArea,SWT.NONE);
libelleCompteLabel.setText("Libell� compte");
libelleCompteText = new Text(workArea, SWT.BORDER);
libelleCompteText.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true,false,1, 1));
// Champs Rib
Label ribCompteLabel = new Label(workArea,SWT.NONE);
ribCompteLabel.setText("R.I.B");
ribCompteText = new Text(workArea, SWT.BORDER);
ribCompteText.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true,false, 1, 1));
// Champs Agence bancaire
Label agenceCompteLabel = new Label(workArea,SWT.NONE);
agenceCompteLabel.setText("Agence bancaire");
agenceCompteText = new Text(workArea, SWT.BORDER);
agenceCompteText.setLayoutData( new GridData(GridData.FILL, GridData.BEGINNING, true,false, 1, 1));
return parent;
}
#Override
protected Control createButtonBar(Composite parent) {
Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
line.setLayoutData(new GridData (SWT.FILL,SWT.TOP,true,false));
final Composite buttonBar = new Composite(parent, SWT.NONE);
buttonBar.setLayoutData(new GridData (SWT.FILL,SWT.TOP,true,false));
final GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.makeColumnsEqualWidth = false;
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
buttonBar.setLayout(layout);
// Create Add button
// Own method as we need to overview the SelectionAdapter
createOkButton(buttonBar, OK, "Add", true);
// Add a SelectionListener
// Create Cancel button
Button cancelButton =
createButton(buttonBar, CANCEL, "Cancel", false);
// Add a SelectionListener
cancelButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setReturnCode(CANCEL);
close();
}
});
return buttonBar;
}
I'm trying to do look alike chess-board in SWT, JAVA.
I tried to do array of buttons, but the color of a button can't be changed (after a long research I have done!).
So I did an array of labels that I can change their color, but now I cannot handle them in one listener, and I don't think that 64 copy-paste listeners are the right thing to do.
However, I found the setActionCommand is not for labels at all.
Do you have any suggestions what can I do to fix that out?
Thanks.
You can use the same Listener for multiple Labels:
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
GridLayout layout = new GridLayout(8, true);
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
shell.setLayout(layout);
shell.setText("Chess");
/* Define listener once */
Listener listener = new Listener()
{
#Override
public void handleEvent(Event event)
{
/* event.widget is the source of the event */
if(event.widget instanceof Label)
{
System.out.println(event.widget.getData());
}
}
};
for(int i = 0; i < 64; i++)
{
Label label = new Label(shell, SWT.CENTER);
label.setText(i + "");
label.setData(i);
/* Use listener here */
label.addListener(SWT.MouseUp, listener);
label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Color background = ((i + (i/8))%2 == 0) ? display.getSystemColor(SWT.COLOR_BLACK) : display.getSystemColor(SWT.COLOR_WHITE);
Color foreground = ((i + (i/8))%2 == 0) ? display.getSystemColor(SWT.COLOR_WHITE) : display.getSystemColor(SWT.COLOR_BLACK);
label.setBackground(background);
label.setForeground(foreground);
}
shell.pack();
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
It will print out the Labels data on click.
Looks like this:
Group group = new Group(parent, SWT.NONE);
StyledText comment = new StyledText(group, SWT.BORDER_DASH);
This creates a group with a text area inside.
How can I later delete the text (remove it from the screen so that I can replace it with something else)?
Use Widget.dispose.
public class DisposeDemo {
private static void addControls(final Shell shell) {
shell.setLayout(new GridLayout());
Button button = new Button(shell, SWT.PUSH);
button.setText("Click to remove all controls from shell");
button.addSelectionListener(new SelectionListener() {
#Override public void widgetDefaultSelected(SelectionEvent event) {}
#Override public void widgetSelected(SelectionEvent event) {
for (Control kid : shell.getChildren()) {
kid.dispose();
}
}
});
for (int i = 0; i < 5; i++) {
Label label = new Label(shell, SWT.NONE);
label.setText("Hello, World!");
}
shell.pack();
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
addControls(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
Another option is to use a StackLayout to switch between underlying controls. This prevents you from running into a "widget is disposed" error.
You have to either call comment.changeParent(newParent) or comment.setVisible(false) to remove/hide it from the Group. I am unsure if comment.changeParent(null) would work but I would give that a try.
We do it this way because SWT uses the Composite Pattern.
group.getChildren()[0].dispose() will remove the first child. You need to find a way to identify the precise child you want to delete. It could be comparing the id. You can do that by using the setData / getData on that control:
For example:
StyledText comment = new StyledText(group, SWT.BORDER_DASH);
comment.setData("ID","commentEditBox");
and then:
for (Control ctrl : group.getChildren()) {
if (control.getData("ID").equals("commentEditBox")) {
ctrl.dispose();
break;
}
}