Java: Howto embed SWT widget into Swing JFrame

Today I wanted to embed a SWT component (Browser) into an existing JFrame. This is the way it works:

  1. import java.awt.BorderLayout;
  2. import java.awt.Canvas;
  3. import java.awt.Dimension;
  4.  
  5. import javax.swing.JFrame;
  6. import javax.swing.JPanel;
  7.  
  8. import org.eclipse.swt.SWT;
  9. import org.eclipse.swt.awt.SWT_AWT;
  10. import org.eclipse.swt.browser.Browser;
  11. import org.eclipse.swt.layout.FillLayout;
  12. import org.eclipse.swt.widgets.Display;
  13. import org.eclipse.swt.widgets.Shell;
  14.  
  15. /**
  16.  * A simple canvas that encapsulates a SWT Browser instance.
  17.  * Add it to a AWT or Swing container and call "connect()" <b>after</b>
  18.  * the container has been made visible.
  19.  */
  20. public class BrowserCanvas extends Canvas {
  21.  
  22. private Thread swtThread;
  23. private Browser swtBrowser;
  24.  
  25. /**
  26.   * Connect this canvas to a SWT shell with a Browser component
  27.   * and starts a background thread to handle SWT events. This method
  28.   * waits until the browser component is ready.
  29.   */
  30. public void connect() {
  31. if (this.swtThread == null) {
  32. final Canvas canvas = this;
  33. this.swtThread = new Thread() {
  34. @Override
  35. public void run() {
  36. try {
  37. Display display = new Display();
  38. Shell shell = SWT_AWT.new_Shell(display, canvas);
  39. shell.setLayout(new FillLayout());
  40.  
  41. synchronized (this) {
  42. swtBrowser = new Browser(shell, SWT.NONE);
  43. this.notifyAll();
  44. }
  45.  
  46. shell.open();
  47. while (!isInterrupted() &amp;&amp; !shell.isDisposed()) {
  48. if (!display.readAndDispatch()) {
  49. display.sleep();
  50. }
  51. }
  52. shell.dispose();
  53. display.dispose();
  54. } catch (Exception e) {
  55. interrupt();
  56. }
  57. }
  58. };
  59. this.swtThread.start();
  60. }
  61.  
  62. // Wait for the Browser instance to become ready
  63. synchronized (this.swtThread) {
  64. while (this.swtBrowser == null) {
  65. try {
  66. this.swtThread.wait(100);
  67. } catch (InterruptedException e) {
  68. this.swtBrowser = null;
  69. this.swtThread = null;
  70. break;
  71. }
  72. }
  73. }
  74. }
  75.  
  76. /**
  77.   * Returns the Browser instance. Will return "null"
  78.   * before "connect()" or after "disconnect()" has
  79.   * been called.
  80.   */
  81. public Browser getBrowser() {
  82. return this.swtBrowser;
  83. }
  84.  
  85. /**
  86.   * Stops the swt background thread.
  87.   */
  88. public void disconnect() {
  89. if (swtThread != null) {
  90. swtBrowser = null;
  91. swtThread.interrupt();
  92. swtThread = null;
  93. }
  94. }
  95.  
  96. /**
  97.   * Ensures that the SWT background thread
  98.   * is stopped if this canvas is removed from
  99.   * it's parent component (e.g. because the
  100.   * frame has been disposed).
  101.   */
  102. @Override
  103. public void removeNotify() {
  104. super.removeNotify();
  105. disconnect();
  106. }
  107.  
  108. /**
  109.   * Opens a new JFrame with BrowserCanvas in it
  110.   */
  111. public static void main(String[] args) {
  112. <b>// <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=161911">Required for Linux systems</a>
  113. System.setProperty("sun.awt.xembedserver", "true");</b>
  114.  
  115. // Create container canvas. Note that the browser
  116. // widget will not be created, yet.
  117. final BrowserCanvas browserCanvas = new BrowserCanvas();
  118. browserCanvas.setPreferredSize(new Dimension(800, 600));
  119. JPanel panel = new JPanel(new BorderLayout());
  120. panel.add(browserCanvas, BorderLayout.CENTER);
  121.  
  122. // Add container to Frame
  123. JFrame frame = new JFrame("My SWT Browser");
  124. frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
  125. frame.setContentPane(panel);
  126. frame.pack();
  127.  
  128. // This is VERY important: Make the frame visible BEFORE
  129. // connecting the SWT Shell and starting the event loop!
  130. frame.setVisible(true);
  131. browserCanvas.connect();
  132.  
  133. // Now we can open a webpage, but remember that we have
  134. // to use the SWT thread for this.
  135. browserCanvas.getBrowser().getDisplay().asyncExec(new Runnable() {
  136. @Override
  137. public void run() {
  138. browserCanvas.getBrowser().setUrl("http://www.google.com");
  139. }
  140. });
  141. }
  142. }

Comments

Hi Cybso,

On my side, under Mac OS X, I was having a blank canvas with Cocoa warnings in console.

The first warning was about the main thread and manage to prevent by adding -XstartOnFirstThread on my VM parameters.

The next warnings was about NSCondition unlocked when not locked.

Please, let me know if you manage to make this works.

Thank you !

Good to know. I'll have to implement this into our software the next weeks. So I'll definitly test this under OS X and update this article.

Really nice!
Does not seem to work under Mac OS X though...

The browser displays only after the empty window is resized!

Hi,
I have similar code and it is working for me - last SWT, last JRE, Windows7. I found your article because I was having problems with it because I was initialising SWT too early - before the Canvas was visible. I'd recommend using addNotify() instead of just putting your connect() call after setVisible() - this would help if you have to hide and re-show it later.

Regards,
Martin

doesn't work on Linux. blank(grey) canvas

Hi Cybso,

I've tried the code snippet in a Win7-x64 machine (with swt-3.7RC4-win32-win32-x86_64) and got the same gray canvas as Gourav and othman.

Doing some research I found out that it's needed to refresh the layout cache which is done beneath the covers when resizing the frame.

I found that the refreshing could be achieved by calling the Shell's methods layout(true) and pack(), but for me it didn't work. The pack() call almost got it going, but made the canvas get resized to a little rectangle on the top left.

There was another "workaround" by calling the frame setSize() method like frame.setSize(frame.getWidth() + 1, frame.getHeight() + 1) but it sounded too ugly.

After some testing I finally got a better solution by calling the Shell methods setFullScreen(true) and next pack() just after the setUrl() inside run().

If there is a better solution, please tell us! Thanks for the code snippet.

-Tony

Hi Cybso,

I am facing a problem while running this example on 32 bit Windows Vista. When I run this example I get a grey screen and a mouse hour glass icon showing that page is loading, but even after waiting for few minutes grey screen remains as it is and google web page do not appears. If I resize the screen by dragging the corner of the grey screen window of the program, I instantly get to see the web page of google.
Can you suggest what can be reason for this? and what can be work around for this? User running this program will not know that resizing window will make web page appear.
I am running this program unchanged in my Eclipse Java EE IDE for Web Developers(version 3.7.0.v20110530-9gF7UHNFFt4cwE-pkZDJ7oz-mj4OSEIlu9SEv0f) with jdk 1.6 update 27.

looking forward to your reply,
Gaurav

Try adding a "frame.setSize(800,600);" as a line
At the last line of d main Method to solve d blank Window problem.

Hello Tony,

i'm afraid I can't help. The code written above was not longer required in our project so we removed it. I have not done any experiments with SWT and Swing since then.

Yes, of course. I consider this code as public domain :-)

This is exactly what i needed, i added this file to my project and it worked. (only tested windows so far, XP and W7)

I want to release my project under the Apache License version 2.0. Can i have your permission to include this code?

Hi,
This is a prefect example to use swt shell inside jframe. i want to know why html page rendering is little bit slow have any idea?
Thanks

Add new comment