I'm working on a Java/Eclipse SWT application that displays and edits map data captured by a special device in a stacked fashion, i.e. there are different layers of "geospatial features" that can be shown/hidden or modified. It was found to be helpful to have an aerial imagery layer which could be easily retrieved e.g. from google maps.
I thought of using the SWT Browser Widget to retrieve and render this satellite view, which actually works like a charm. The Problem is that I need to have a hidden Browser Widget which would work in the background and return me a swt.graphics.Image etc. of the rendered content or even better directly use a given GC for drawing.
I also thought about simpler solutions but there are two restrictions:
I can't just use static maps from Google because the map tile I need would have to be larger than they allow and the partial reloading that they provide (e.g. when moving the map view port) would also be very handy.
I can't simply feed my data into Google Maps for several reasons.
So in general: How do I have a (hidden) instance of a Browser Widget render its output to an Image/GC instead of the screen. Is there something else except from the Browser Widget which could do the job?
I think you can use the org.eclipse.swt.widgets.Control.print(GC) method to print a control to an image. I have not tried it for Browser though.
Here is sample to start with using SWT Browser control
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("Browser Test");
shell.setSize(500, 500);
shell.setLayout(new GridLayout(1,false));
final Browser browser = new Browser(shell, SWT.NONE);
browser.setUrl("https://maps.google.com/maps?hl=en&tab=wl");
//browser.setVisible(false);
browser.setLayoutData(new GridData(GridData.FILL_BOTH));
Button b = new Button(shell, SWT.NONE);
b.setText("Show");
b.addListener(SWT.Selection, new Listener() {
#Override
public void handleEvent(Event event) {
Image img = new Image(display, 500, 500);
GC gc = new GC(img);
browser.print(gc);
gc.dispose();
showImage(img);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static class ImageDialog extends Dialog
{
private Image img;
protected ImageDialog(Shell parentShell,Image img) {
super(parentShell);
this.img = img;
}
#Override
protected Control createDialogArea(Composite parent) {
Composite comp = (Composite) super.createDialogArea(parent);
Label lbl = new Label(comp,SWT.NONE);
lbl.setImage(img);
return comp;
}
#Override
protected void okPressed() {
img.dispose();
super.okPressed();
}
}
protected static void showImage(Image img) {
ImageDialog dialog = new ImageDialog(Display.getDefault().getActiveShell(), img);
dialog.open();
}
another approach that we can think of
user capture div as image and save to computer
execute java script into SWT Browser Browser.execute(java script) to capture div into an image.
Related
I'm working on an RCP application that's in a transition from a Swing version. So we have a lot of UI components that still need to live in the Swing world during this transition. I'm able to properly place the existing Swing components in AWT-SWT bridge frames.
I've wrapped these Swing components in a JScrollable pane before adding them to the bridge so that I don't have to resize the containing-part when the size of the Swing UI elements change. The code where I place an old Swing component in a part looks like this:
#PostConstruct
public void postConstruct(final Composite parent) {
/* Create embedding composite */
final Frame bridgeFrame;
final Composite embed;
embed = new Composite(parent, SWT.EMBEDDED);
embed.setLayout(new FillLayout());
bridgeFrame = SWT_AWT.new_Frame(embed);
bridgeFrame.setLayout(new BorderLayout());
bridgeFrame.add(
new JScrollPane(getTestPanel()),
BorderLayout.CENTER);
}
My Swing component has a behavior where when the user clicks a button, things that were hidden in the component are made visible, or new UI elements are added to the Swing component. For example:
private JPanel getTestPanel() {
final JPanel output;
final JButton eastBttn, westBttn;
output = new JPanel();
eastBttn = new JButton("East Button");
westBttn = new JButton("West Button");
output.setLayout(new BorderLayout());
output.add(eastBttn, BorderLayout.EAST);
output.add(westBttn, BorderLayout.WEST);
eastBttn.addActionListener(evt -> {
System.out.println("East Button Clicked");
output.add(new JLabel("East Button Clicked"), BorderLayout.CENTER);
});
return output;
}
My problem is, when the elements in the Swing-component change, the parent bridge-frame doesn't properly get rendered.
When the parts are first created, my application looks like this:
After I click on the EastButton it's supposed to add a text label in the center of that bridge frame. However, nothing changes in the application view.
But, when I even begin to resize the containing part-sash a little, the part containing the bridge-frame updates correctly:
What can I do to make the bridge-frame update containing part update automatically?
To test whether this was a repainting issue on the bridge-frame, I had a menu item which would trigger a repaint / revalidate / pack of the bridge-frame, but that didn't solve the issue. I suspect it has something to do with the renderer of the containing part, but have no idea how to go about addressing it.
The same a problem exists in a pure Swing solution:
public static void main(String[] args) {
JFrame bridgeFrame = new JFrame("Test");
bridgeFrame.setSize(400, 400);
bridgeFrame.setLayout(new BorderLayout());
bridgeFrame.add(new JScrollPane(getTestPanel()), BorderLayout.CENTER);
bridgeFrame.setVisible(true);
}
You need to add an output.doLayout() in your event handler.
I eventually got around the problem by attaching a custom ControlListener / ComponentListener to the part containing the bridge. If any changes within the workings of the bridge-frame caused it to resize to beyond the parent, I'd have the listener resize it to fit within the parent thus forcing the scroll-pane to take over.
Here's my listener:
public class BridgeComponetAdapter
extends ComponentAdapter
implements ControlListener {
private final Composite parent;
private final Frame bridgeFrame;
private Point parentSize;
public BridgeComponetAdapter(
final Composite parent,
final Frame bridgeFrame) {
this.parent = parent;
this.bridgeFrame = bridgeFrame;
bridgeFrame.addComponentListener(this);
parent.addControlListener(this);
}
#Override
public void componentResized(final ComponentEvent e) {
System.out.println(e);
if (e.getSource() != bridgeFrame)
return;
final Dimension currentBridgeSize;
currentBridgeSize = bridgeFrame.getSize();
if (currentBridgeSize.getWidth() > parentSize.x
|| currentBridgeSize.getHeight() > parentSize.y) {
bridgeFrame.setSize(parentSize.x, parentSize.y);
}
}
#Override
public void controlMoved(final ControlEvent e) {}
#Override
public void controlResized(final ControlEvent e) {
System.out.println(e);
if (e.getSource() == parent)
parentSize = parent.getSize();
}
}
It's not an elegant solution; I'm still open to other ideas to solve the problem.
It's my first question here but I'm really stuck. Maybe it's just my extreme fatigue for last few days, but I looked up google for few hours now and couldn't find any close to good answer.
I know SWT is event driven like all GUI's I can think of and when creating widgets I should keep in mind that they need to be able to reach the ones they are supposed to modify/interact with. But what I need to know is if my thinking is right and if not what should I improve.
Let's say I start with eclipse+windowbuilder+swt/jface java project. Then I add button and clabel +click listener for button(SelectionListener), so the generated code looks more or less like(only main method, above there is only Main class and imports)
public static void main(String[] args) {
Display display = Display.getDefault();
Shell shell = new Shell();
shell.setSize(450, 300);
shell.setText("SWT Application");
Button btnNewButton = new Button(shell, SWT.NONE);
btnNewButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
}
});
btnNewButton.setBounds(51, 31, 75, 25);
btnNewButton.setText("New Button");
CLabel lblOneTwo = new CLabel(shell, SWT.NONE);
lblOneTwo.setBounds(180, 119, 61, 21);
lblOneTwo.setText("one two");
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
As I know and it's probably obvious to most of you I can't just go and add to
btnNewButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
}
});
code like lblOneTwo.setText("three") and from what I know and use sometimes I just declare all stuff beforehand, as static widget widgetName and then I can access them from basically everywhere, so code like
static Button btnNewButton;
static CLabel lblOneTwo;
public static void main(String[] args) {
Display display = Display.getDefault();
Shell shell = new Shell();
shell.setSize(450, 300);
shell.setText("SWT Application");
btnNewButton = new Button(shell, SWT.NONE);
btnNewButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
lblOneTwo.setText("three");
}
});
(...)
works just fine. But I guess and think it's not the best practice and way of doing it, isn't it? So please, help me, point me in the right direction so I can stop coding in a sin. Thanks in advance! indirect answers with links to articles/tutorials would be great, but I like examples people are putting here because of their clear way of showing things.
There are multiple ways to do this.
Make the widgets field of your class. Don't make them static unless necessary.
private Label l;
private Button b;
public static void main(String[] args)
{
...
b.addListener(...);
Define all your widgets first (have to be final if you want to use them in the Listener), after that add your Listeners.
final Label l = ...;
final Button b = ....;
b.addListener(...);
If you want to change the widget itself in the Listener you can use Event#widget to get the source of the event.
b.addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event e)
{
Button button = (Button) e.widget;
}
});
I am creating a custom tooltip where i have a textbox.I am able to do that but i could not get balloon like icon as in the attached picture .Can anyone help me regarding this.
Mytooltip class:
public class MyToolTip extends ToolTip {
private Shell parentShell;
public MyToolTip(Control control) {
super(control,SWT.BALLOON,false);
this.parentShell = control.getShell();
}
#Override
protected Composite createToolTipContentArea(Event event, Composite parent) {
// TODO Auto-generated method stub
Composite comp = new Composite(parent,SWT.NONE);
comp.setLayout(new FillLayout());
Text text = new Text(comp,SWT.BORDER);
text.setText("");
return comp;
}
}
Class using tooltip:
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new RowLayout(SWT.VERTICAL));
Text text = new Text(shell, SWT.BORDER);
text.setText("sample text field");
MyToolTip myTooltipLabel = new MyToolTip(text);
myTooltipLabel.setShift(new Point(-5, -5));
myTooltipLabel.setHideOnMouseDown(false);
myTooltipLabel.activate();
myTooltipLabel.setRespectDisplayBounds(false);
myTooltipLabel.setRespectMonitorBounds(false);
The problem is, that you are using org.eclipse.jface.window.ToolTip whereas the code that was used to create that screenshot uses org.eclipse.swt.widgets.ToolTip.
The SWT tooltip can have the balloon look by passing SWT.BALLOON as the style bit.
The JFace tooltip does not support SWT.BALLOON, only ToolTip.NO_RECREATE and ToolTip.RECREATE.
So here is the conclusion: You can't subclass the swt tooltip to make it editable. You can't make the JFace tooltip look the way you want it to look. The only solution that is left is creating your own Widget based on Composite or Canvas that does what you want.
So I am creating a image to place in the title area. Everything works with the exception that only a 1/4th of the image is displayed?
my image is actually text and a image combine in one image EX: JKTeater [ ] <-- icon
so right now only JKT is showing in the title area
Here is the create() method
public void create() {
super.create();
setTitle("JKTeater Application");
setMessage("Hello World");
if (image != null) setTitleImage(image);
}
Is there a specific size that the title area code allows for?
Is there a way to place the end of the image at the end of the title area?
Can you use a layout to move it around?
How can I get a black horizonal line at the bottom of the title area?
EDIT
I am sure that it would be asking to much to see if you can actually change the background color from a basic color to a gradient
Here is an example TitleAreaDialog. As you can see, the Image is completely shown and aligned to the right:
public static void main(String[] args) {
final Shell shell = new Shell();
shell.setLayout(new FillLayout());
TitleAreaDialog dialog = new MyTitleAreaDialog(shell);
dialog.setTitleAreaColor(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGB());
dialog.open();
}
private static class MyTitleAreaDialog extends TitleAreaDialog
{
private Image image;
public MyTitleAreaDialog(Shell parentShell) {
super(parentShell);
image = new Image(Display.getDefault(), "/home/baz/Desktop/StackOverflow.png");
}
#Override
public boolean close() {
if (image != null)
image.dispose();
return super.close();
}
#Override
protected Control createContents(Composite parent) {
Control contents = super.createContents(parent);
setTitle("Title");
setMessage("Message");
if (image != null)
setTitleImage(image);
return contents;
}
#Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
// YOUR LINE HERE!
Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
line.setLayoutData(new GridData(SWT.FILL, SWT.END, true, true));
return composite;
}
}
Is there a specific size that the title area code allows for?
AFAIK there are no restrictions to the size. I tried using an Image that was larger than my screen resolution and it was fully displayed. The Dialog was obviously unusable though.
I am sure that it would be asking to much to see if you can actually change the background color from a basic color to a gradient
The background color can be changed using dialog.setTitleAreaColor(RGB) (in this case the widget background color), but you cannot use a gradient. There is a deprecated method getTitleArea() which would return the title area Composite, but I really wouldn't recommend using that.
How can I get a black horizonal line at the bottom of the title area?
The line at the bottom was achieved by using:
Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
line.setLayoutData(new GridData(SWT.FILL, SWT.END, true, true));
Can you use a layout to move it around?
There is a similar question here:
Moving an image of a TitleAreaDialog to the left
The answers there explain how to change details of the TitleAreaDialog. Maybe read up on them.
I'm updating a Java Swing application to support the user switching the app's font from normal size to a larger size (so the user can switch between the two sizes at runtime). One problem I'm having is with a JTree that uses HTML for the tree nodes to underline the text in some nodes (the HTML is just embedded in the JLabel of each tree node). One extra thing to know about the nodes is that they're a custom component, adding a JCheckBox in front of each JLabel.
The problem is that once the JTree is visible, increasing the font size causes the nodes (containing underlined text) to not resize. The HTML for those nodes seems to prevent the node from becoming wider, so when the font changes, the text becomes truncated.
I think my options are to either: 1) use another approach to make the text underlined, since removing the HTML from the JLabel causes it to resize properly when the font size changes, or 2) keep the HTML formatting and somehow force the JTree/JLabels to resize when the font size is updated (possibly by firing a property change event?).
The code already calls SwingUtilities.updateComponentTreeUI() on the parent JFrame when the font size gets updated.
EDIT: The method used to change the font in the application is explained here.
Any help would be greatly appreciated. Thanks in advance!
-Mike
I cannot recreate the problem you describe. Here is a test program that works for me on JavaSE 6:
public class JTreeFontResize {
private static JTree tree;
private static JFrame frame;
public static void main(String[] args) throws InterruptedException,
InvocationTargetException {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
tree = new JTree(new Object[] { "One (plain)",
"<html><u>Two (HTML)", "<html>Three (HTML)" });
frame = new JFrame("Tree Font Resize");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100, 100, 300, 300);
frame.add(tree);
frame.setVisible(true);
}
});
Thread.sleep(2000);
changeFontSize(20);
Thread.sleep(2000);
changeFontSize(30);
Thread.sleep(2000);
changeFontSize(12);
}
private static void changeFontSize(final int size) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Font font = new Font("Vernanda", Font.PLAIN, size);
FontUIResource fontResource = new FontUIResource(font);
Enumeration<Object> keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof FontUIResource) {
UIManager.put(key, fontResource);
}
}
SwingUtilities.updateComponentTreeUI(frame);
}
});
}
}
If the above works for you then maybe you should post a cut down version of your problematic code.