I am creating a custom toolbar for my RCP application.
As shown in figure I want to have a drop down box with three other text boxes. These are basically the input box and are interdependent. Right now each of these boxes are in separate classes. I want to bring them together in one class so it is easier to create listeners for each other.
protected void fillCoolBar(ICoolBarManager coolBar) {
IToolBarManager toolbar = new ToolBarManager(coolBar.getStyle());
coolBar.add(toolbar);
Toolbar extraToolBar = new Toolbar("Toolbar");
toolbar.add(extraToolBar);
toolbar.add(new Separator());
toolbar.add(new MyCombo("Demo Combo box"));
toolbar.add(new Separator());
toolbar.add(new IPaddress("Ip"));
toolbar.add(new Separator());
toolbar.add(new Mask("Mask"));
toolbar.add(new Separator());
toolbar.add(new Count("Count"));
}
public class IPaddress extends ControlContribution {
Text textBox;
public IPaddress(String id) {
super(id);
// TODO Auto-generated constructor stub
}
#Override
protected Control createControl(Composite parent) {
textBox = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.WRAP);
textBox.setLayoutData(new GridData(GridData.FILL_BOTH));
textBox.addModifyListener(new ModifyListener(){
public void modifyText(ModifyEvent event) {
Text text = (Text) event.widget;
System.out.println(text.getText());
}
});
return textBox;
}
}
Thus I want to create a new custom Toolbar will all the functionalities that I want and then stick it to the original. But somehow it only shows an empty bar on the left.
protected Control createControl(Composite parent) {
toolBar = new ToolBar(parent, SWT.FLAT |SWT.BORDER);
Device dev = toolBar.getDisplay();
try {
newi = new Image(dev, "C:\\Users\\RahmanAs\\ChipcoachWorkspace\\ChipCoach\\icons\\FileClose.png");
opei = new Image(dev, "C:\\Users\\RahmanAs\\ChipcoachWorkspace\\ChipCoach\\icons\\FileOpen.png");
} catch (Exception e) {
System.out.println("Cannot load images");
System.out.println(e.getMessage());
System.exit(1);
}
ToolItem item0 = new ToolItem (toolBar, SWT.PUSH);
item0.setImage(newi);
item0.setText("Hello");
ToolItem item1 = new ToolItem(toolBar, SWT.PUSH);
item1.setText("Push");
ToolItem item2 = new ToolItem(toolBar, SWT.PUSH);
item2.setText("Pull");
return toolBar;
}
I also have run buttons, which I created in the plugin using Vogella's tutorial. But I cannot program their placements in this way. (For example if I want them in the beginning.) Is there a way to create them programmatically?
I think the reason your leftmost ToolBar is empty is a layout issue. In my code below, I had a similar "empty" ToolBar problem when I did not have any buttons located outside the custom ToolBar but still in the main ToolBar. Adding in the "foo" and "bar" buttons fixed the layout issue, but I could not figure out the right calls to layout() or pack() to fix it. I think this may be related to the bug here.
I took a swing at creating a similar ToolBar and built around the "RCP Mail Template" plugin-project that you can create from the "New Plug-in Project" wizard.
To address your first two concerns, I created 3 packages in the example RCP bundle (I called my project "com.bar.foo"):
com.bar.foo.actions - Contains classes that extend ContributionControl and wrap Combo and Text widgets. These have nothing to do with the data model and just worry about creating widgets.
com.bar.foo.model - Contains the data model. I just made up a simple model here with an IP, mask, gateway, and one or two helpful methods.
com.bar.foo.toolBar - These classes are plugged up to the main UI ToolBar via the org.eclipse.ui.menus extension point. They link the data model to the ContributionControls in the first package. The most important class here is ToolBarContribution, which effectively centralizes all of your listeners. This makes it easier for you to link the listeners for the widgets to the same model.
Here's the source for the ToolBarContribution (note that it addresses your first two concerns because it hooks up the listeners to the model and provides its own ToolBar to the UI):
package com.bar.foo.toolBar;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
import com.bar.foo.actions.ComboContributionItem;
import com.bar.foo.actions.TextContributionItem;
import com.bar.foo.model.NetworkConfig;
public class ToolBarContribution extends WorkbenchWindowControlContribution {
// Our data model.
private NetworkConfig configuration = new NetworkConfig();
// Each of these corresponds to a widget in the ToolBar.
private Action scanAction;
private ComboContributionItem sourceCombo;
private TextContributionItem ipText;
private TextContributionItem maskText;
private TextContributionItem gatewayText;
#Override
protected Control createControl(Composite parent) {
setupContributionItems();
// Let's not get our hands messy with SWT... add IActions or
// IContributionItems to a ToolBarManager and let the ToolBarManager
// create the SWT ToolBar.
ToolBarManager manager = new ToolBarManager();
manager.add(scanAction);
manager.add(sourceCombo);
manager.add(ipText);
manager.add(maskText);
manager.add(gatewayText);
ToolBar toolBar = manager.createControl(parent);
// Highlight the ToolBar in red.
toolBar.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_RED));
return toolBar;
}
private void setupContributionItems() {
scanAction = new Action("Scan Host") {
#Override
public void run() {
System.out.println("Scanning...");
String host = sourceCombo.getComboControl().getText();
configuration.scanHost(host);
System.out.println("Scanned!");
refreshTexts();
}
};
scanAction.setToolTipText("Scans the host for a configuration.");
final SelectionListener comboListener = new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
ipText.getTextControl().setText("");
maskText.getTextControl().setText("");
gatewayText.getTextControl().setText("");
}
};
sourceCombo = new ComboContributionItem("sourceCombo") {
#Override
public Control createControl(Composite parent) {
// Let ComboContributionItem create the initial control.
Control control = super.createControl(parent);
// Now customize the Combo widget.
Combo combo = getComboControl();
combo.setItems(configuration.getAvailableHosts());
combo.addSelectionListener(comboListener);
// Return the default control.
return control;
}
};
ipText = new TextContributionItem("ipText", SWT.BORDER | SWT.SINGLE
| SWT.READ_ONLY);
maskText = new TextContributionItem("maskText");
gatewayText = new TextContributionItem("gatewayText");
}
private void refreshTexts() {
ipText.getTextControl().setText(configuration.getIP());
maskText.getTextControl().setText(configuration.getMask());
gatewayText.getTextControl().setText(configuration.getGateway());
}
}
In addition to this ToolBar, I have two separate buttons in the main UI ToolBar, one before, and one after the custom ToolBar. Their sources are in the package com.bar.foo.toolBar. Here is the first command:
package com.bar.foo.toolBar;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
public class FooHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
System.out.println("foo");
return null;
}
}
And here is the second one:
package com.bar.foo.toolBar;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
public class BarHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
System.out.println("bar");
return null;
}
}
Since I didn't know too much about your data, I had to create my own model. The model in the package com.bar.foo.model is just one class:
package com.bar.foo.model;
public class NetworkConfig {
private String ip = "";
private String mask = "";
private String gateway = "";
public String[] getAvailableHosts() {
return new String[] { "fooHost" };
}
public void scanHost(String host) {
if ("fooHost".equals(host)) {
ip = "192.168.1.2";
mask = "255.255.255.0";
gateway = "192.168.1.1";
} else {
ip = "";
mask = "";
gateway = "";
}
}
public String getIP() {
return ip;
}
public String getMask() {
return mask;
}
public String getGateway() {
return gateway;
}
}
Now for the com.bar.foo.actions package that contains the ControlContributions that go in the custom ToolBar. Note that neither of these two classes have anything to do with the model, and they can be re-used elsewhere in your product.
The first class just wraps a Combo widget. The widget can be initially customized by overriding the controlCreated(Combo) method. I use that in the ToolBarContribution class to add a SelectionListener and set the Combo's items. Here's the class:
package com.bar.foo.actions;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
public class ComboContributionItem extends ControlContribution {
private Combo combo;
public ComboContributionItem(String id) {
super(id);
}
#Override
protected Control createControl(Composite parent) {
combo = new Combo(parent, SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL);
return combo;
}
#Override
public int computeWidth(Control control) {
// The widget is now 100 pixels. You can new GC gc = new GC(control) and
// use the gc.stringExtent(String) method to help compute a more dynamic
// width.
return 100;
}
public Combo getComboControl() {
return combo;
}
}
The other class in this package wraps a Text widget:
package com.bar.foo.actions;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
public class TextContributionItem extends ControlContribution {
private final int style;
private Text text;
public TextContributionItem(String id) {
this(id, SWT.BORDER | SWT.SINGLE);
}
public TextContributionItem(String id, int style) {
super(id);
this.style = style;
}
#Override
protected Control createControl(Composite parent) {
text = new Text(parent, style);
return text;
}
#Override
public int computeWidth(Control control) {
return 100;
}
public Text getTextControl() {
return text;
}
}
I didn't do this, but if you need to further customize the Text widget for your ToolBar, you can override the createControl(Composite) method just like I did when initializing the ComboContributionItem.
Now one last thing: I used extensions to customize the ToolBar. However, the same logic used by ToolBarContribution applies to your fillCoolBar(ICoolBarManager) method or your createControl(Composite) method, depending on which ToolBar you ultimately wish to modify.
In my case, here's what I added to the end of the plugin's plugin.xml:
<extension
point="org.eclipse.ui.menus">
<menuContribution
locationURI="toolbar:org.eclipse.ui.main.toolbar">
<toolbar
id="com.bar.foo.toolbar">
<command
commandId="com.bar.foo.commands.foo"
label="Foo"
style="push">
</command>
<control
class="com.bar.foo.toolBar.ToolBarContribution">
</control>
<command
commandId="com.bar.foo.commands.bar"
label="Bar"
style="push">
</command>
</toolbar>
</menuContribution>
</extension>
<extension
point="org.eclipse.ui.commands">
<command
id="com.bar.foo.commands.foo"
name="Foo">
</command>
<command
id="com.bar.foo.commands.bar"
name="Bar">
</command>
</extension>
<extension
point="org.eclipse.ui.handlers">
<handler
class="com.bar.foo.toolBar.FooHandler"
commandId="com.bar.foo.commands.foo">
</handler>
<handler
class="com.bar.foo.toolBar.BarHandler"
commandId="com.bar.foo.commands.bar">
</handler>
</extension>
The commands are hooked up so that there's a button for FooHandler before the custom ToolBar and a button for BarHandler after the custom ToolBar. The order in which these commands are specified in the xml will be reflected in the application. Likewise, the order in which the items are added to the custom ToolBar will reflect in your product.
Another note on placement: You can make the menuContributions appear in different places by setting a placement in the locationURI's query, e.g., toolbar:org.eclipse.ui.main.toolbar?after=additions. "before" is another placement keyword like "after". More examples of this can be found in this Eclipse help doc.
Related
I want to create a custom Dialog, which just displays options (see figure 1). If the user selects one of those options, the dialog should close and return the corresponding result instantly.
So far, I can only accomplish this by adding an arbitrary ButtonType to the Dialog, hiding it by using setVisible(false) and applying fire() in the EventHandler of the clicked option.
This weird workaround actually works fine, but seems to me very unprofessional ...
So, how to do this in a more professional or proper way without using the ButtonType trick?
My workaround-code looks like this (Dialog class):
public class CustomDialog extends Dialog<String> {
private static final String[] OPTIONS
= new String[]{"Option1", "Option2", "Option3", "Option4"};
private String selectedOption = null;
Button applyButton;
public CustomDialog() {
super();
initStyle(StageStyle.DECORATED);
VBox vBox = new VBox();
for (String option : OPTIONS) {
Button optionButton = new Button(option);
optionButton.setOnAction((event) -> {
selectedOption = option;
applyButton.fire();
});
vBox.getChildren().add(optionButton);
}
getDialogPane().setContent(vBox);
getDialogPane().getButtonTypes().add(ButtonType.APPLY);
applyButton = (Button) getDialogPane().lookupButton(ButtonType.APPLY);
applyButton.setVisible(false);
setResultConverter((dialogButton) -> {
return selectedOption;
});
}
}
Using the dialog class:
CustomDialog dialog = new CustomDialog();
Optional<String> result = dialog.showAndWait();
String selected = null;
if (result.isPresent()) {
selected = result.get();
} else if (selected == null) {
System.exit(0);
}
A Dialog is just a window displaying a DialogPane, and, quoting the Javadocs for DialogPane:
DialogPane operates on the concept of ButtonType. A ButtonType is a
descriptor of a single button that should be represented visually in
the DialogPane. Developers who create a DialogPane therefore must
specify the button types that they want to display
(my emphasis). Therefore, while you've shown one possible workaround and in the other answer Slaw has shown another, if you're trying to use a Dialog without using ButtonType and its associated result converter, you're really using the Dialog class for something for which it's not intended.
The functionality you describe is perfectly achievable with a regular modal Stage. For example, the following gives the same basic behavior you describe and involves no ButtonTypes:
package org.jamesd.examples.dialog;
import java.util.Optional;
import java.util.stream.Stream;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class CustomDialog {
private static final String[] OPTIONS
= {"Option 1", "Option 2", "Option 3", "Option 4"};
private final Stage stage ;
private String selectedOption = null ;
public CustomDialog() {
this(null);
}
public CustomDialog(Window parent) {
var vbox = new VBox();
// Real app should use an external style sheet:
vbox.setStyle("-fx-padding: 12px; -fx-spacing: 5px;");
Stream.of(OPTIONS)
.map(this::createButton)
.forEach(vbox.getChildren()::add);
var scene = new Scene(vbox);
stage = new Stage();
stage.initOwner(parent);
stage.initModality(Modality.WINDOW_MODAL);
stage.setScene(scene);
}
private Button createButton(String text) {
var button = new Button(text);
button.setOnAction(e -> {
selectedOption = text ;
stage.close();
});
return button ;
}
public Optional<String> showDialog() {
selectedOption = null ;
stage.showAndWait();
return Optional.ofNullable(selectedOption);
}
}
Here's a simple application class which uses this custom dialog:
package org.jamesd.examples.dialog;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
var root = new VBox();
// Real app should use an external style sheet:
root.setStyle("-fx-padding: 12px; -fx-spacing: 5px;");
var showDialog = new Button("Show dialog");
var label = new Label("No option chosen");
showDialog.setOnAction(e ->
new CustomDialog(stage)
.showDialog()
.ifPresentOrElse(label::setText, Platform::exit));
root.getChildren().addAll(showDialog, label);
stage.setScene(new Scene(root));
stage.show();
}
}
As pointed out by both #Sedrick and #James_D, the Dialog API is built around the concept of "button types". Not using ButtonType goes against the API and, because of this, will always seem hacky/wrong. That said, there is a slight alteration you could make to your current code that satisfies your "without using any 'ButtonType'-controls" goal. It doesn't appear to be documented, but if you set the result property manually it triggers the close-and-return-result process. This means you don't need to add any ButtonType and can bypass the resultConverter completely. Here's a proof-of-concept:
OptonsDialog.java:
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.layout.VBox;
public class OptionsDialog<T extends OptionsDialog.Option> extends Dialog<T> {
public interface Option {
String getDisplayText();
}
#SafeVarargs
public OptionsDialog(T... options) {
if (options.length == 0) {
throw new IllegalArgumentException("must provide at least one option");
}
var content = new VBox(10);
content.setAlignment(Pos.CENTER);
content.setPadding(new Insets(15, 25, 15, 25));
for (var option : options) {
var button = new Button(option.getDisplayText());
button.setOnAction(
event -> {
event.consume();
setResult(option);
});
content.getChildren().add(button);
}
getDialogPane().setContent(content);
}
}
App.java:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.Window;
public class App extends Application {
private enum Foo implements OptionsDialog.Option {
OPTION_1("Option Number 1"),
OPTION_2("Option Number 2"),
OPTION_3("Option Number 3"),
OPTION_4("Option Number 4");
private final String displayText;
Foo(String displayText) {
this.displayText = displayText;
}
#Override
public String getDisplayText() {
return displayText;
}
}
#Override
public void start(Stage primaryStage) {
var button = new Button("Click me!");
button.setOnAction(
event -> {
event.consume();
showChosenOption(primaryStage, promptUserForOption(primaryStage));
});
primaryStage.setScene(new Scene(new StackPane(button), 500, 300));
primaryStage.show();
}
private static Foo promptUserForOption(Window owner) {
var dialog = new OptionsDialog<>(Foo.values());
dialog.initOwner(owner);
dialog.setTitle("Choose Option");
return dialog.showAndWait().orElseThrow();
}
private static void showChosenOption(Window owner, OptionsDialog.Option option) {
var alert = new Alert(AlertType.INFORMATION);
alert.initOwner(owner);
alert.setHeaderText("Chosen Option");
alert.setContentText(String.format("You chose the following: \"%s\"", option.getDisplayText()));
alert.show();
}
}
It's not that different from your current workaround and it's still working against the API. This also relies on undocumented behavior (that setting the result property manually closes the dialog and returns the result). The ButtonBar at the bottom still takes up some space, though less than when you add an invisible button. It's possible, however, to get rid of this empty space by adding the following CSS:
.options-dialog-pane .button-bar {
-fx-min-height: 0;
-fx-pref-height: 0;
-fx-max-height: 0;
}
Note the above assumes you've modified the code to add the "options-dialog-pane" style class to the DialogPane used with the OptionsDialog.
I think you should read the following from the Java Docs:
Dialog Closing Rules:
It is important to understand what happens when a Dialog is closed, and also how a Dialog can be closed, especially in abnormal closing situations (such as when the 'X' button is clicked in a dialogs title bar, or when operating system specific keyboard shortcuts (such as alt-F4 on Windows) are entered). Fortunately, the outcome is well-defined in these situations, and can be best summarised in the following bullet points:
JavaFX dialogs can only be closed 'abnormally' (as defined above) in
two situations:
When the dialog only has one button, or
When the dialog has multiple buttons, as long as one of them meets one of the following requirements:
The button has a ButtonType whose ButtonBar.ButtonData is of type ButtonBar.ButtonData.CANCEL_CLOSE.
The button has a ButtonType whose ButtonBar.ButtonData returns true when ButtonBar.ButtonData.isCancelButton() is called.
In all other situations, the dialog will refuse to respond to all close requests, remaining open until the user clicks on one of the available buttons in the DialogPane area of the dialog.
If a dialog is closed abnormally, and if the dialog contains a button which meets one of the two criteria above, the dialog will attempt to set the result property to whatever value is returned from calling the result converter with the first matching ButtonType.
If for any reason the result converter returns null, or if the dialog is closed when only one non-cancel button is present, the result property will be null, and the showAndWait() method will return Optional.empty(). This later point means that, if you use either of option 2 or option 3 (as presented earlier in this class documentation), the Optional.ifPresent(java.util.function.Consumer) lambda will never be called, and code will continue executing as if the dialog had not returned any value at all.
If you don't mind the Buttons being horizontal, you should use ButtonType and setResultConverter to return a String based on which button is pressed.
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.stage.StageStyle;
import javafx.util.Callback;
/**
*
* #author blj0011
*/
public class CustomDialog extends Dialog<String>
{
String result = "";
public CustomDialog()
{
super();
initStyle(StageStyle.DECORATED);
setContentText(null);
setHeaderText(null);
ButtonType buttonOne = new ButtonType("Option1");
ButtonType buttonTwo = new ButtonType("Option2");
ButtonType buttonThree = new ButtonType("Option3");
ButtonType buttonFour = new ButtonType("Option4");
getDialogPane().getButtonTypes().addAll(buttonOne, buttonTwo, buttonThree, buttonFour);
setResultConverter(new Callback<ButtonType, String>()
{
#Override
public String call(ButtonType param)
{
if (param == buttonOne) {
return buttonOne.getText();
}
else if (param == buttonTwo) {
return buttonTwo.getText();
}
else if (param == buttonThree) {
return buttonThree.getText();
}
else if (param == buttonFour) {
return buttonFour.getText();
}
return "";
}
});
}
}
Update: As #Slaw stated in the comments, you can replace setResultConverter(...) with setResultConverter(ButtonType::getText).
I'm searching for a way to add an overlay over some composites in my application. The overlay will contain an label with text "No data available". The underlying composite need to be shown but the user cannot do anything. My application contains different composite part in one screen so I need a way to only place the overlay over one of the composites. Is there a way to implement this in SWT?
A possible solution would be to put a semi-transparent Shell with no trimmings over the Composite you want to cover.
The tricky part is to update the overlay Shell to continuously match the size, position and visibility of the Composite and its parents (since they also could affect the children bounds and visibility).
So I decided to try to make a class Overlay to do that; it can be used to cover any Control and it uses control and paint listeners to track and match the underlying Control. These listeners are also attached to the whole hierarchy of parents of the Control.
You can set the color, the transparency and a text over the Overlay using the corresponding methods.
I made some simple tests and it seemed to work correctly, but I can't guarantee anything. You might want to give it a try it.
A simple example using it:
public class OverlayTest {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout(SWT.VERTICAL));
shell.setSize(250, 250);
// create the composite
Composite composite = new Composite(shell, SWT.NONE);
composite.setLayout(new FillLayout(SWT.VERTICAL));
// add stuff to the composite
for (int i = 0; i < 5; i++) {
new Text(composite, SWT.BORDER).setText("Text " + i);
}
// create the overlay over the composite
Overlay overlay = new Overlay(composite);
overlay.setText("No data available");
// create the button to show/hide the overlay
Button button = new Button(shell, SWT.PUSH);
button.setText("Show/hide overlay");
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent arg0) {
// if the overlay is showing we hide it, otherwise we show it
if (overlay.isShowing()) {
overlay.remove();
}
else {
overlay.show();
}
}
});
shell.open();
while (shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
And the Overlay class:
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
/**
* A customizable overlay over a control.
*
* #author Loris Securo
*/
public class Overlay {
private List<Composite> parents;
private Control objectToOverlay;
private Shell overlay;
private Label label;
private ControlListener controlListener;
private DisposeListener disposeListener;
private PaintListener paintListener;
private boolean showing;
private boolean hasClientArea;
private Scrollable scrollableToOverlay;
public Overlay(Control objectToOverlay) {
Objects.requireNonNull(objectToOverlay);
this.objectToOverlay = objectToOverlay;
// if the object to overlay is an instance of Scrollable (e.g. Shell) then it has
// the getClientArea method, which is preferable over Control.getSize
if (objectToOverlay instanceof Scrollable) {
hasClientArea = true;
scrollableToOverlay = (Scrollable) objectToOverlay;
}
else {
hasClientArea = false;
scrollableToOverlay = null;
}
// save the parents of the object, so we can add/remove listeners to them
parents = new ArrayList<Composite>();
Composite parent = objectToOverlay.getParent();
while (parent != null) {
parents.add(parent);
parent = parent.getParent();
}
// listener to track position and size changes in order to modify the overlay bounds as well
controlListener = new ControlListener() {
#Override
public void controlMoved(ControlEvent e) {
reposition();
}
#Override
public void controlResized(ControlEvent e) {
reposition();
}
};
// listener to track paint changes, like when the object or its parents become not visible (for example changing tab in a TabFolder)
paintListener = new PaintListener() {
#Override
public void paintControl(PaintEvent arg0) {
reposition();
}
};
// listener to remove the overlay if the object to overlay is disposed
disposeListener = new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
remove();
}
};
// create the overlay shell
overlay = new Shell(objectToOverlay.getShell(), SWT.NO_TRIM);
// default values of the overlay
overlay.setBackground(objectToOverlay.getDisplay().getSystemColor(SWT.COLOR_GRAY));
overlay.setAlpha(200);
// so the label can inherit the background of the overlay
overlay.setBackgroundMode(SWT.INHERIT_DEFAULT);
// label to display a text
// style WRAP so if it is too long the text get wrapped
label = new Label(overlay, SWT.WRAP);
// to center the label
overlay.setLayout(new GridLayout());
label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
showing = false;
overlay.open();
overlay.setVisible(showing);
}
public void show() {
// if it's already visible we just exit
if (showing) {
return;
}
// set the overlay position over the object
reposition();
// show the overlay
overlay.setVisible(true);
// add listeners to the object to overlay
objectToOverlay.addControlListener(controlListener);
objectToOverlay.addDisposeListener(disposeListener);
objectToOverlay.addPaintListener(paintListener);
// add listeners also to the parents because if they change then also the visibility of our object could change
for (Composite parent : parents) {
parent.addControlListener(controlListener);
parent.addPaintListener(paintListener);
}
showing = true;
}
public void remove() {
// if it's already not visible we just exit
if (!showing) {
return;
}
// remove the listeners
if (!objectToOverlay.isDisposed()) {
objectToOverlay.removeControlListener(controlListener);
objectToOverlay.removeDisposeListener(disposeListener);
objectToOverlay.removePaintListener(paintListener);
}
// remove the parents listeners
for (Composite parent : parents) {
if (!parent.isDisposed()) {
parent.removeControlListener(controlListener);
parent.removePaintListener(paintListener);
}
}
// remove the overlay shell
if (!overlay.isDisposed()) {
overlay.setVisible(false);
}
showing = false;
}
public void setBackground(Color background) {
overlay.setBackground(background);
}
public Color getBackground() {
return overlay.getBackground();
}
public void setAlpha(int alpha) {
overlay.setAlpha(alpha);
}
public int getAlpha() {
return overlay.getAlpha();
}
public boolean isShowing() {
return showing;
}
public void setText(String text) {
label.setText(text);
// to adjust the label size accordingly
overlay.layout();
}
public String getText() {
return label.getText();
}
private void reposition() {
// if the object is not visible, we hide the overlay and exit
if (!objectToOverlay.isVisible()) {
overlay.setBounds(new Rectangle(0, 0, 0, 0));
return;
}
// if the object is visible we need to find the visible region in order to correctly place the overlay
// get the display bounds of the object to overlay
Point objectToOverlayDisplayLocation = objectToOverlay.toDisplay(0, 0);
Point objectToOverlaySize;
// if it has a client area, we prefer that instead of the size
if (hasClientArea) {
Rectangle clientArea = scrollableToOverlay.getClientArea();
objectToOverlaySize = new Point(clientArea.width, clientArea.height);
}
else {
objectToOverlaySize = objectToOverlay.getSize();
}
Rectangle objectToOverlayBounds = new Rectangle(objectToOverlayDisplayLocation.x, objectToOverlayDisplayLocation.y, objectToOverlaySize.x,
objectToOverlaySize.y);
Rectangle intersection = objectToOverlayBounds;
// intersect the bounds of the object with its parents bounds so we get only the visible bounds
for (Composite parent : parents) {
Rectangle parentClientArea = parent.getClientArea();
Point parentLocation = parent.toDisplay(parentClientArea.x, parentClientArea.y);
Rectangle parentBounds = new Rectangle(parentLocation.x, parentLocation.y, parentClientArea.width, parentClientArea.height);
intersection = intersection.intersection(parentBounds);
// if intersection has no size then it would be a waste of time to continue
if (intersection.width == 0 || intersection.height == 0) {
break;
}
}
overlay.setBounds(intersection);
}
}
I have one org.eclipse.ui.dialogs.CheckedTreeSelectionDialog like below code.
final CheckedTreeSelectionDialog checkedTreeSelectionDialog = new
CheckedTreeSelectionDialog(new Shell(),new ActionLabelProvider(), new
ActionContentProvider());
ActionLabelProvider implements org.eclipse.jface.viewers.ILabelProvider and ActionContentProvider implements org.eclipse.jface.viewers.ITreeContentProvider
I have some elements initially selected and some are not in the CheckedTreeSelectionDialog.
When dialog is open, if I check the unchecked element, I want to show one message.
How can I do this?
Create a subclass of CheckedTreeSelectionDialog and add addCheckStateListener, use the below code.
// When user checks a checkbox in the tree
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog;
public class MyCheckedTreeSelectionDialog extends CheckedTreeSelectionDialog {
public MyCheckedTreeSelectionDialog(Shell parent, ILabelProvider labelProvider, ITreeContentProvider contentProvider) {
super(parent, labelProvider, contentProvider);
}
#Override
protected CheckboxTreeViewer getTreeViewer() {
CheckboxTreeViewer treeViewer = super.getTreeViewer();
treeViewer.addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
if (event.getChecked()) {
// Given element is checked
} else {
// Given element is un-checked
// Your message here
}
}
});
return treeViewer;
}
}
I want to add vertical scroll bar to the screen that comes out of the below code. can you please suggest how it can be done?
public class SampleDialog extends TrayDialog {
public SampleDialog(final Shell shell) {
super(shell);
this.shell = shell;
}
#Override
public void create() {
super.create();
}
#Override
protected Control SampleDialog(final Composite parent) {
final GridLayout layout = new GridLayout();
layout.numColumns = 1;
parent.setLayout(layout);
createSampleText(parent);
createSampleCombo(parent);
}
}
where:
org.eclipse.jface.dialogs.TrayDialog;
org.eclipse.swt.layout.GridLayout;
org.eclipse.swt.widgets.Composite;
You can use a ScrolledComposite as the main parent for all your child controls in the dialog.
Some helpful snippets can be found here.
e.g. I'd like to show one given string(not fixed one) in one view of my Eclipse plugin,how to do it?thx.
bb#feijiao.info
If you follow the RCP tutorial, you will see that you can define your own view:
package de.vogella.rcp.intro.view;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;
public class MyView extends ViewPart {
#Override
public void createPartControl(Composite parent) {
Text text = new Text(parent, SWT.BORDER);
text.setText("Imagine a fantastic user interface here");
}
#Override
public void setFocus() {
}
}
That will give you a View with a custom text.
alt text http://www.vogella.de/articles/RichClientPlatform/images/addview200.gif
If you keep a reference to the org.eclipse.swt.widgets.Text used to display some text, you can change that text.
my solution from VonC's thought.
//below codes are working for View.
//variable to keep reference to Canvas
private Canvas canvas = null;
...
//copy
public void createPartControl(Composite parent) {
Canvas canvas = new Canvas(parent, SWT.BORDER |
SWT.NO_MERGE_PAINTS | SWT.NONE );
this.canvas = canvas;
}
//...
//one getter method to get canvas
public Canvas getCanvas(){
return this.canvas;
}
//////////////////////////////////
//////////////////////////////////
//below codes are working in PopupMenu's action
page.showView("org.act.bpel2automata.views.GraphView");
IViewPart view = page.findView("org.act.bpel2automata.views.GraphView");
//GraphView is defined by myself,
if(view instanceof GraphView){
GraphView gView = (GraphView)view;
Canvas canvas = gView.getCanvas();
}
//other operations,like draw lines or sth.
...