| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
| <html><head><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Swing/SWT Integration</title><link href="../article.css" rel="stylesheet" type="text/css"><meta content="DocBook XSL Stylesheets V1.71.1" name="generator"><meta name="description" content="Swing and SWT are sometimes seen as strictly competing technologies. Some people have strong opinions on which UI toolkit to use exclusively for client applications. However, in the real world, ideological extremes are often impractical. Some valid use cases require both technologies to coexist in a single application. While mixing the two toolkits is not a simple task, it can be done, and it can be done such that the two toolkits are smoothly integrated. This article discusses the steps necessary to achieve good Swing/SWT integration. It focuses on the use case of embedding existing Swing components into an SWT-based Rich Client Platform application."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><h1>Swing/SWT Integration</h1><div class="summary"><h2>Summary</h2><p> |
| Swing and SWT are sometimes seen as strictly competing technologies. Some |
| people have strong opinions on which UI toolkit to use exclusively for client |
| applications. However, in the real world, ideological extremes are |
| often impractical. Some valid use cases require both technologies |
| to coexist in a single application. While mixing the two toolkits is not a |
| simple task, it can be done, and it can be done such that the two toolkits |
| are smoothly integrated. This article discusses the steps necessary to |
| achieve good Swing/SWT integration. It focuses on the use case of embedding |
| existing Swing components into an SWT-based Rich Client Platform application. |
| </p><div class="author"> |
| By |
| Gordon Hirsch, |
| SAS Institute Inc.<br></div><div class="copyright"> |
| Copyright © |
| 2007 SAS Institute Inc. Made available under the EPL v1.0 </div><div class="date"><span class="date">June 20, 2007<br></span></div></div><div class="content"><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-introduction"></a>Introduction</h2></div></div></div><p> |
| Many existing standalone Java clients represent a large investment in Swing |
| components. While there are compelling arguments for moving these clients to the Eclipse Rich |
| Client Platform (RCP), the full migration of a large existing application can be |
| expensive. By retaining some Swing components, the initial cost of |
| migration can be reduced. Over time, Swing components may be incrementally converted |
| to SWT if and when it becomes necessary. |
| </p><p> |
| For example, at SAS we have extended the standard Swing <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JTable.html" target="_new"> |
| <code class="code">JTable</code></a> component to |
| create an enhanced table that meets the needs of our existing applications. |
| A large effort will be required to convert this component to SWT. Instead of |
| waiting for an equivalent SWT table, we are able to use the Swing |
| table component as-is in an RCP application, as shown in the screenshot below. |
| It shows an RCP application running on the Windows XP platform. |
| The two tables are Swing components; all other components |
| were created with SWT. |
| </p><div class="figure"><a name="fig-table-embed"></a><p class="title"><b>Figure 1. Embedding Swing Table Components</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/table-embed.png" alt="Embedding Swing Table Components"></div></div></div><br class="figure-break"><p> |
| We also have a large inventory of complex Swing components to display graphs and charts. |
| Again, by mixing Swing and SWT, we can consider the conversion of these Swing components separately from |
| the immediate needs of an application that we'd like to migrate to RCP. |
| The screenshot below shows another RCP application with a Swing-based graph |
| component. Everything else in the image is an |
| SWT component. (This application may not look like an RCP application, |
| but it is. The unusual look is due to extensive use of the Eclipse |
| presentations API and other advanced workbench features.) |
| </p><div class="figure"><a name="fig-graph-embed"></a><p class="title"><b>Figure 2. Embedding Graphical Swing Components</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/graph-embed.jpg" alt="Embedding Graphical Swing Components"></div></div></div><br class="figure-break"><p> |
| The use of Swing components with the RCP implies that two large UI toolkits will coexist in a |
| single application and that some amount of integration is necessary to ensure UI |
| consistency across the entire application. |
| </p><p> |
| SWT provides the SWT/AWT Bridge [<a href="#bib-bridge">2</a>] as the basic infrastructure for Swing/SWT integration. |
| However, the bridge is only the first step along the |
| path to embedding usable Swing components in RCP applications. Much of |
| this article is devoted to explaining the additional practices necessary for |
| effective integration. Without these practices, the |
| SWT/AWT Bridge is insufficient for production-quality application use. |
| With them, you can successfully host key Swing components in RCP |
| applications. |
| </p><p> |
| Integrating two independent UI toolkits is a complicated task, and it |
| rarely produces perfect results. This article |
| concentrates on practical techniques (and yes, even some hacks) to make the |
| integration effective enough for use in real-world applications. |
| </p><p> |
| Despite the complexity, most of the practices described below can be encapsulated |
| in a single common base class to be used when embedding Swing components. There are additional |
| helper classes to support the implementation. An example is included with this |
| article. See <a href="#app-example-code" title="A. Example Code">Appendix A, <i>Example Code</i></a> for more information. |
| </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-using-bridge"></a>Using the SWT/AWT Bridge</h2></div></div></div><p> |
| The SWT/AWT Bridge has been part of SWT since version 3.0. It is |
| a very simple API, located in the package |
| <code class="code"> |
| <a href="http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/awt/package-summary.html" target="_new"> |
| org.eclipse.swt.awt</a>. |
| </code> |
| </p><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| This article focuses on embedding AWT frames inside SWT composites. It demonstrates only |
| one half of the SWT/AWT Bridge. Nevertheless, most of the improvements described below also apply to the |
| other direction: embedding SWT composites inside AWT frames. |
| </p></td></tr></table></div><p>Minimally, embedding an AWT frame inside an SWT composite is just two simple lines of code</p><pre class="programlisting"> |
| Composite composite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); |
| Frame frame = SWT_AWT.new_Frame(composite); |
| </pre><p> |
| An instance of <code class="code">org.eclipse.swt.Composite</code> is created with the <code class="code">SWT.EMBEDDED</code> |
| style. This style signals that an AWT frame is to be embedded inside the Composite. The call to the static <code class="code">new_Frame</code> |
| method creates and returns such a frame. The frame may then be |
| populated with AWT and/or Swing components. |
| </p><p> |
| The returned frame is not a standard AWT frame. By default, it is a subclass of <code class="code">java.awt.Frame</code> |
| that is meant to be embedded within native applications. In fact, it is the same frame that is used to |
| embed <a href="http://java.sun.com/applets/" target="_new">applets</a> inside a browser window. |
| </p><p> |
| The example code shown above is deceptively simple. While SWT does much under the covers to manage the |
| integration of the two toolkits, the scope of the bridge's implementation is very narrow. In reality, you must do much |
| more in your application to make the integration more consistent. These additional steps are |
| described below in <a href="#sec-adding-to-bridge" title="Building on the SWT/AWT Bridge">the section called “Building on the SWT/AWT Bridge”</a>. |
| </p><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-platform"></a>Platform Considerations</h3></div></div></div><p> |
| There are version constraints for using the SWT/AWT Bridge that differ from platform to |
| platform. |
| </p><div class="table"><a name="version-table"></a><p class="title"><b>Table 1. Minimum Versions</b></p><div class="table-contents"><table summary="Minimum Versions" border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Platform</th><th align="left">JRE Version</th><th align="left">Eclipse Version</th></tr></thead><tbody><tr><td align="left">Windows</td><td align="left">1.4</td><td align="left">3.0</td></tr><tr><td align="left">Linux/GTK</td><td align="left">1.5</td><td align="left">3.0</td></tr><tr><td align="left">Mac OS X</td><td align="left">1.5.0 Release 4</td><td align="left">3.2</td></tr></tbody></table></div></div><br class="table-break"><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| Mac OS X also requires installation of the SWT Compatibility |
| Libraries. See the SWT |
| <a href="http://www.eclipse.org/swt/faq.php#swtawtosx" target="_new">FAQ</a> |
| for details. Also, as of the writing of this article, the SWT/AWT Bridge on |
| Mac OS X is not yet complete. See bug |
| <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=145890" target="_new"> |
| 145890 |
| </a> |
| for details. |
| </p></td></tr></table></div><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| As of the writing of this article, use of the SWT/AWT Bridge causes hangs |
| with Java 6 with the GTK look and feel on the Linux/GTK platform. Refer to |
| Eclipse bug |
| <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=91157" target="_new">91157</a> |
| and Sun bug |
| <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6386791" target="_new"> |
| 6386791 |
| </a> |
| for more information. |
| </p></td></tr></table></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-event-threads"></a>Multiple Event Threads</h3></div></div></div><p> |
| Swing/SWT integration has important threading implications. Each UI toolkit has its own |
| event queue, and each event queue is processed by a separate thread. Most SWT |
| APIs must be called from the SWT event thread. Swing has similar |
| restrictions though they are not as strictly enforced. |
| This split is the major drawback of mixing the toolkits, and it adds some |
| complexity to the code. |
| </p><p> |
| Applications must be aware of the current thread, and, where necessary, |
| schedule tasks to run on the appropriate UI toolkit thread. To schedule work on |
| the AWT event thread, use: |
| </p><div class="itemizedlist"><ul type="disc"><li> |
| <code class="code">javax.swing.SwingUtilities.invokeLater()</code> |
| </li><li> |
| <code class="code">javax.swing.SwingUtilities.invokeAndWait()</code> |
| </li></ul></div><p>To schedule work on the SWT event thread, use:</p><div class="itemizedlist"><ul type="disc"><li> |
| <code class="code">org.eclipse.swt.widgets.Display.asyncExec()</code> |
| </li><li> |
| <code class="code">org.eclipse.swt.widgets.Display.syncExec()</code> |
| </li></ul></div><p> |
| These are the same APIs used in a single-toolkit environment |
| to keep the UI responsive while offloading long running operations to a worker |
| thread. With Swing/SWT integration they are used for the |
| additional purpose of moving work from one event thread to another. |
| </p><p> |
| The use of multiple event threads increases the risk of deadlock. |
| Whenever possible, try to avoid blocking one event thread while |
| scheduling work on the other event thread. In other words, |
| avoid calling <code class="code">SwingUtilities.invokeAndWait</code> from the SWT event |
| thread and avoid calling <code class="code">Display.syncExec</code> from the AWT event thread. |
| Otherwise, if there's ever a case where one blocking call is made while the |
| other thread has made its own blocking call in the other direction, |
| deadlock will occur. |
| </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-adding-to-bridge"></a>Building on the SWT/AWT Bridge</h2></div></div></div><p> |
| If you use the SWT/AWT Bridge API alone, Swing components will integrate poorly |
| into your application. The following sections |
| describe the integration problems in detail and suggest solutions. All of the solutions |
| involve additional code to help manage the Swing/SWT integration. The |
| good news is that this code can be encapsulated in a single custom |
| embedded composite widget (and some support classes), and it can be decoupled from |
| the rest of the application |
| code. |
| </p><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-look-and-feel"></a>Configuring the Swing Look and Feel</h3></div></div></div><p> |
| Here's an example showing some very basic visual problems that result from |
| minimal use of the SWT/AWT Bridge on the Windows platform. |
| </p><div class="figure"><a name="fig-badembed"></a><p class="title"><b>Figure 3. Minimal Embedding of a Swing Component</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/badembed.jpg" alt="Minimal Embedding of a Swing Component"></div></div></div><br class="figure-break"><p> |
| There are some immediately obvious problems here. The header and scrollbars on the Swing |
| <code class="code">JTable</code> (upper right) differ from the SWT-based Error Log view. |
| </p><p> |
| In this example, the <code class="code">JTable</code> is displayed with the standard |
| Swing cross-platform (Metal) |
| <a href="http://java.sun.com/docs/books/tutorial/ui/features/plaf.html" target="_new"> |
| look and feel |
| </a>. |
| On the other hand, the |
| SWT components use underlying native widgets |
| which have a native look and feel. Swing's |
| look and feel must be changed to match the |
| native platform. Since the SWT/AWT Bridge itself does not automatically set |
| the native look and feel, it's necessary to do it yourself. For |
| example, |
| </p><pre class="programlisting"> |
| import javax.swing.UIManager; |
| ... |
| UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); |
| ... |
| </pre><p> |
| Make this call once, before any |
| AWT or Swing components are created. |
| </p><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Tip]" src="images/tip.gif"></td><td><p> |
| Linux/GTK developers: depending on the window manager in use, the Swing system |
| look and feel may not be set to the GTK look and feel. It may be necessary to |
| set the GTK look and feel more explicitly: |
| <pre class="programlisting"> |
| UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); |
| </pre> |
| </p></td></tr></table></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-root-pane-container"></a>Creating a Root Pane Container</h3></div></div></div><p> |
| The |
| <code class="code">SWT_AWT.new_Frame()</code> |
| method returns an instance of a subclass of <code class="code">java.awt.Frame</code> that has been embedded within an SWT |
| <code class="code">Composite</code>. There are certain rules that must be followed when using |
| an embedded frame in Swing. |
| </p><div class="itemizedlist"><ul type="disc"><li><p> |
| The first child of the embedded frame must be a heavyweight component. The |
| component must fill the entire frame. This heavyweight component allows for correct mouse locations and |
| interactions. See Sun bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522" target="_new">4982522</a> |
| for more information. |
| </p></li><li><p> |
| To make the frame viable for use with Swing, you must also create a |
| <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JRootPane.html" target="_new"> |
| root pane |
| </a>container (<code class="code">javax.swing.RootPaneContainer</code>). The root pane is the basis for all Swing windows. It provides the layering |
| capabilities on which all Swing components depend. |
| </p></li><li><p> |
| Due to assumptions in the Swing implementation, the root pane container |
| should be an instance of <code class="code">JFrame</code>, <code class="code">JDialog</code>, <code class="code">JWindow</code>, or <code class="code">JApplet</code>. |
| Of these options, |
| only <code class="code">JApplet</code> is an appropriate choice, since it is the only one that can |
| be embedded. |
| </p></li></ul></div><p> |
| To satisfy each of these rules, create a <code class="code">JApplet</code> as the only child of the |
| embedded frame. Use of <code class="code">JApplet</code> does not imply that you have created a true |
| applet with an applet's lifecycle; you are simply using the same display |
| container as an applet embedded in a browser. |
| </p><pre class="programlisting"> |
| Composite composite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); |
| Frame frame = SWT_AWT.new_Frame(composite); |
| JApplet applet = new JApplet(); |
| frame.add(applet); |
| </pre><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| The embedded frame is a window (a subclass of <code class="code">java.awt.Window</code>). As such, |
| it consumes resources. For this reason we encourage the use of embedded |
| frames as replacements for larger components rather than smaller ones. |
| </p></td></tr></table></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-reducing-flicker"></a>Reducing Flicker</h3></div></div></div><p> |
| Use of the SWT/AWT Bridge, without additional measures, causes excessive |
| flicker in an embedded AWT frame while the application window is resized. The |
| reasons include: |
| </p><div class="itemizedlist"><ul type="disc"><li><p> |
| A heavyweight component (<code class="code">javax.swing.JApplet</code>, in our case) is present in the component |
| hierarchy. The AWT implementation, by default, clears the component's background on every resize event. |
| </p></li><li><p> |
| More resize events are handled by the embedded frame, when compared to the default |
| behavior in a standalone Swing application. This increase is due to the way resize |
| events are passed on to the AWT frame from the enclosing SWT composite. |
| </p></li></ul></div><p> |
| On Windows, set the property |
| <code class="code">sun.awt.noerasebackground</code> to reduce flicker. This undocumented property |
| disables much of the repetitive |
| background clearing by the AWT implementation. It should be set before any |
| AWT or Swing components are instantiated. |
| </p><pre class="programlisting"> |
| System.setProperty("sun.awt.noerasebackground", "true"); |
| </pre><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| The <code class="code">sun.awt.noerasebackground</code> property is not used on non-Windows platforms. |
| Reducing resize flicker on those platforms remains an open problem. |
| </p></td></tr></table></div><p> |
| Though it is necessary, setting |
| <code class="code">sun.awt.noerasebackground</code> |
| has negative consequences. Leaving the background uncleared may result in the |
| temporary display of previously drawn pixels (an example of what is sometimes called |
| <a href="http://inside-swt.blogspot.com/2006/08/swt-speak-part-1.html" target="_new">cheese</a>) |
| during resize operations and before the initial Swing contents |
| are displayed. |
| </p><p> |
| These effects are removed by adding a resize listener to the parent |
| SWT composite. When the composite is resized larger, exposing an unpainted |
| region, the listener fills it immediately with the background color. The |
| cheese is removed immediately, providing a cosmetic improvement during any delay |
| while the embedded Swing component repaints. |
| </p><pre class="programlisting"> |
| class CleanResizeListener extends ControlAdapter { |
| private Rectangle oldRect = null; |
| public void controlResized(ControlEvent e) { |
| // Prevent garbage from Swing lags during resize. Fill exposed areas |
| // with background color. |
| Composite composite = (Composite)e.widget; |
| Rectangle newRect = composite.getClientArea(); |
| if (oldRect != null) { |
| int heightDelta = newRect.height - oldRect.height; |
| int widthDelta = newRect.width - oldRect.width; |
| if ((heightDelta > 0) || (widthDelta > 0)) { |
| GC gc = new GC(composite); |
| try { |
| gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta); |
| gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height); |
| } finally { |
| gc.dispose(); |
| } |
| } |
| } |
| oldRect = newRect; |
| } |
| } |
| </pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-tab-traversal"></a>Tab Traversal</h3></div></div></div><p> |
| Another concern of Swing/SWT integration is the behavior of the tab |
| key when traversing between components from different toolkits. |
| Additional work is needed on the AWT side of the SWT/AWT |
| bridge to solve this problem. To achieve proper tab traversal, implement a custom |
| subclass of <code class="code">java.awt.FocusTraversalPolicy</code> for the embedded AWT frame. |
| The custom policy may delegate to a standard policy when |
| tabbing within the frame, but it must transfer control to an SWT |
| component when: |
| </p><div class="orderedlist"><ol type="1"><li> |
| tabbing on the last component in the frame |
| </li><li> |
| back-tabbing on the first component in the frame |
| </li></ol></div><p> |
| Implementing the forward and backward traversals in the custom policy is simple. |
| However, implementing a proper <code class="code">getDefaultComponent</code> method is tricky. It |
| must return null when nothing in the embedded AWT frame has focus. This behavior is a hack based on |
| assumptions about how AWT will call the method, and it is necessary to avoid an immediate |
| transfer of focus back to AWT after traversing to SWT. For more information, refer to the comments |
| in the class <code class="code">EmbeddedChildFocusTraversalPolicy</code> in <a href="#app-example-code" title="A. Example Code">Appendix A, <i>Example Code</i></a>. |
| </p><p> |
| See the Swing documentation for the |
| <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/doc-files/FocusSpec.html" target="_new">AWT Focus Subsystem</a> |
| for more information on focus traversal policies. |
| </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-modality"></a>Modal Dialogs</h3></div></div></div><p> |
| When a |
| modal dialog is opened from a Swing component, it must be modal across the |
| entire application. Since SWT and AWT have separate event threads, |
| such dialogs are not modal by default. The SWT event thread must |
| be explicitly disabled during the time that a Swing modal dialog is showing. |
| This effect is most easily achieved by opening a modal SWT dialog while the |
| Swing dialog is showing. |
| </p><pre class="programlisting"> |
| java.awt.Dialog awtDialog = ... |
| Shell shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.NO_TRIM); |
| shell.setSize(0, 0); |
| shell.addFocusListener(new FocusAdapter() { |
| public void focusGained(FocusEvent e) { |
| awtDialog.requestFocus(); |
| awtDialog.toFront(); |
| } |
| }); |
| </pre><p> |
| The 0 x 0 size prevents the shell from being seen. The focus listener makes |
| sure that control is restored to the Swing dialog if the SWT window somehow gains focus. |
| (For example, the SWT window may gain focus when the user navigates to your application |
| through some window managers.) |
| </p><div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| On Linux/GTK, even a zero-sized SWT dialog may appear as a small dot |
| on the screen. Hide it by adding the following code to |
| <code class="code">focusGained()</code>. This addition is unnecessary under Windows and |
| actually causes a flash, so make the call only when required. |
| </p><pre class="programlisting"> |
| shell.moveBelow(null); |
| </pre></td></tr></table></div><p> |
| The code shown above ensures correct modal behavior, but how should it be invoked? |
| The easiest approach is to invoke it wherever Swing modal dialogs are created. |
| However, this solution won't work if your Swing component library cannot be |
| modified, or if you cannot introduce dependencies on SWT code from |
| the Swing component library. |
| </p><p> |
| Alternatively, correct modal behavior is enforced more cleanly by installing a listener for all AWT |
| window events. Whenever it is detected that a Swing modal dialog is open or visible, a |
| SWT modal dialog must be opened. The listener is sketched below and is |
| fully implemented in the example code. See <a href="#app-example-code" title="A. Example Code">Appendix A, <i>Example Code</i></a> for more |
| information. |
| </p><pre class="programlisting"> |
| class AwtDialogListener implements AWTEventListener, ComponentListener { |
| |
| private final Display display; |
| |
| AwtDialogListener(Display display) { |
| this.display = display; |
| Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.WINDOW_EVENT_MASK); |
| } |
| |
| private void handleRemovedDialog(Dialog awtDialog, boolean removeListener) { |
| // An AWT dialog has been closed or set invisible. Close the SWT dialog |
| ... |
| } |
| |
| private void handleAddedDialog(final Dialog awtDialog) { |
| // An AWT dialog has been opened or set visible. Open the SWT dialog |
| // and add this as a component listener on the dialog to catch |
| // visibility changes |
| ... |
| } |
| |
| private void handleOpenedWindow(WindowEvent event) { |
| Window window = event.getWindow(); |
| if (window instanceof Dialog) { |
| handleAddedDialog((Dialog)window); |
| } |
| } |
| |
| private void handleClosedWindow(WindowEvent event) { |
| // Dispose-based close |
| Window window = event.getWindow(); |
| if (window instanceof Dialog) { |
| // Remove dialog and component listener |
| handleRemovedDialog((Dialog)window, true); |
| } |
| } |
| |
| private void handleClosingWindow(WindowEvent event) { |
| // System-based close |
| // (see example code for full implementation) |
| ... |
| } |
| |
| public void eventDispatched(AWTEvent event) { |
| switch (event.getID()) { |
| case WindowEvent.WINDOW_OPENED: |
| handleOpenedWindow((WindowEvent)event); |
| break; |
| |
| case WindowEvent.WINDOW_CLOSED: |
| handleClosedWindow((WindowEvent)event); |
| break; |
| |
| case WindowEvent.WINDOW_CLOSING: |
| handleClosingWindow((WindowEvent)event); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| public void componentHidden(ComponentEvent e) { |
| Object obj = e.getSource(); |
| if (obj instanceof Dialog) { |
| // Remove dialog but keep listener in place so that we know if/when it is set visible |
| handleRemovedDialog((Dialog)obj, false); |
| } |
| } |
| |
| public void componentShown(ComponentEvent e) { |
| Object obj = e.getSource(); |
| if (obj instanceof Dialog) { |
| handleAddedDialog((Dialog)obj); |
| } |
| } |
| ... |
| } |
| </pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-dismissing-popups"></a>Dismissing Pop-up Menus</h3></div></div></div><p> |
| When a context menu is displayed in an embedded AWT frame, the menu does not |
| disappear after clicking outside the frame. This AWT limitation is well known, |
| but it is especially noticeable in embedded frames since it may result in |
| multiple visible pop-ups within the same SWT shell. |
| </p><div class="figure"><a name="fig-double-popup"></a><p class="title"><b>Figure 4. Double Popups</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/double-popup.jpg" alt="Double Popups"></div></div></div><br class="figure-break"><p> |
| This issue is documented in Sun bugs |
| <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4311449" target="_new">4311449</a> |
| and <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4290715" target="_new">4290715</a>, |
| among others. |
| </p><p>There are partial workarounds to this problem:</p><div class="itemizedlist"><ul type="disc"><li><p> |
| <span class="emphasis"><em>Manually dismiss Swing pop-ups when the user activates other windows.</em></span> Install a |
| listener for window focus events on the embedded frame. On a focus lost event, the window |
| and component hierarchies of the frame can be searched for instances of |
| <code class="code">javax.swing.JPopupMenu</code>. |
| <div class="note" style="margin-left: 0.38in; margin-right: 0.38in;"><table class="note-table"><tr><td><img alt="[Note]" src="images/note.gif"></td><td><p> |
| This workaround applies to JRE version 1.4 and earlier; it should not be |
| necessary if you are using version 1.5 or later. |
| </p></td></tr></table></div> |
| </p></li><li><p> |
| <span class="emphasis"><em>Manually dismiss Swing pop-ups when the user activates an SWT menu.</em></span> Install |
| a display filter to listen for <code class="code">SWT.Show</code> events which are generated whenever an |
| SWT menu is shown. In the filter's event handler, visible Swing pop-ups can be dismissed as described |
| above. |
| </p></li></ul></div><p> |
| Despite these workarounds, some issues remain; for example, Swing pop-ups are not |
| dismissed when the user interacts with the titlebar on the workbench window. |
| </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-system-settings"></a>Synchronizing with System Settings</h3></div></div></div><p> |
| When the user changes font settings through the Windows control panel, the |
| changes are not always properly propagated to Swing components. This problem is |
| especially noticeable when integrating with SWT since the SWT components do, in fact, |
| recognize these changes. To work around the problem, manually |
| change the font in the Swing Windows Look and Feel when the system font has changed. |
| </p><p> |
| Fortunately, in Eclipse 3.2, a new SWT event has been added to allow applications |
| to detect changes to system settings. So, Swing font changes can be triggered |
| as follows |
| </p><pre class="programlisting"> |
| Display display = ... |
| Listener settingsListener = new Listener() { |
| public void handleEvent(Event event) { |
| handleSettingsChange(); |
| } |
| }; |
| display.addListener(SWT.Settings, settingsListener); |
| </pre><p> |
| Font changes for Swing components are best handled through the |
| look and feel, rather than updating the font for individual components. |
| </p><pre class="programlisting"> |
| private void handleSettingsChange() { |
| ... |
| org.eclipse.swt.graphics.Font currentSystemFont = ...; |
| FontData fontData = currentSystemFont.getFontData()[0]; |
| |
| int resolution = Toolkit.getDefaultToolkit().getScreenResolution(); |
| int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0); |
| |
| // The style constants for SWT and AWT map exactly, and since they are int constants, they should |
| // never change. So, the SWT style is passed through as the AWT style. |
| java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize); |
| |
| FontUIResource fontResource = new FontUIResource(awtFont); |
| |
| UIManager.put("Button.font", fontResource); |
| UIManager.put("CheckBox.font", fontResource); |
| UIManager.put("ComboBox.font", fontResource); |
| ... // many more similar calls |
| |
| Container contentPane = ... // content pane from the root pane |
| SwingUtilities.updateComponentTreeUI(contentPane); |
| } |
| } |
| </pre><p> |
| First, the SWT font is converted to an equivalent AWT font. |
| AWT font sizes always assume a 72 dpi resolution. The true screen resolution must be |
| used to convert the platform font size into an AWT point size that matches when displayed. |
| Then, the font for various Swing component types is changed via the look and feel's |
| <code class="code">javax.swing.UIManager</code>. |
| </p><p> |
| See the example code (<a href="#app-example-code" title="A. Example Code">Appendix A, <i>Example Code</i></a>) for the complete implementation. |
| </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-keystroke-contention"></a>Keystroke Contention</h3></div></div></div><p> |
| Unexpected results can occur if you map the same keystroke to a global action |
| in your RCP application and to a handler in an embedded Swing component. |
| Keystrokes defined through the <code class="code"><a href="http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_ui_bindings.html" target="_new"> |
| org.eclipse.ui.bindings</a></code> extension point will take precedence over those |
| defined in the Swing component, even if the Swing component has focus. In this case, the |
| RCP binding is managed through a Display filter, so the keystroke is consumed before |
| it reaches the Swing component at all. As a result, there is no simple, general |
| workaround to avoid this problem at the level of the embedded composite. Instead, you |
| can avoid these conflicts by: |
| <div class="itemizedlist"><ul type="disc"><li><p> |
| Using the <code class="code"><a href="http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_ui_contexts.html" target="_new"> |
| org.eclipse.ui.contexts</a></code> extension point to organize key bindings that should |
| not be visible when the Swing component is in focus. |
| </p></li><li><p> |
| Replicate the Swing component's action in an RCP action, bound to the same keystroke. The |
| binding for RCP action can be introduced through an <code class="code">org.eclipse.ui.contexts</code> |
| extension. The action can be implemented to invoke the underlying the Swing action. This |
| approach may be necessary anyway, if you want the action to be available anywhere outside |
| the Swing component (for example, in the application's main menu). |
| </p></li></ul></div> |
| </p><p> |
| There are also occasional issues with keystroke contention between the embedded Swing |
| component and the window system. For example, hitting Shift-F10 from inside many Swing |
| components opens a context (popup) menu. In Windows, when there is no context menu, the default |
| processing of the F10 keystroke |
| (with or without shifting) transfers focus to the application's main menu bar. |
| When you have embedded Swing components, these two behaviors conflict. |
| The Swing component properly handles Shift-F10, showing the popup, but the containing SWT shell does |
| not know it; therefore, it reports the keystroke to Windows as unhandled. Windows then |
| transfers focus to the main menu bar, and the popup loses focus. |
| </p><p> |
| Fortunately, this conflict can be resolved. Here's some code which exploits the fact that |
| the default Windows behavior happens when F10 is released, rather than when it is pressed. |
| The embedded composite can install a KeyListener and consume the release of the |
| Shift-F10 keystroke so that Windows never sees it. |
| </p><pre class="programlisting"> |
| public void keyReleased(KeyEvent e) { |
| if (e.keyCode == SWT.F10 && (e.stateMask & SWT.SHIFT) != 0) { |
| e.doit = false; |
| } |
| } |
| </pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-other-workarounds"></a>Other Workarounds</h3></div></div></div><p> |
| Due to Sun bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6411042" target="_new">6411042</a>, |
| when AWT/Swing components are |
| added to an embedded frame from the SWT event thread, a memory leak occurs. To |
| avoid the leak, it is best to create and add these components from the AWT event thread |
| instead. Simply invoke the creation code through |
| <code class="code">javax.swing.SwingUtilities.invokeLater()</code>. |
| As discussed earlier, it is a good general habit to call Swing APIs from the |
| AWT event thread anyway. |
| </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-unresolved-issues"></a>Unresolved Issues</h2></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-cleartype"></a>ClearType</h3></div></div></div><p> |
| On Windows under JRE 1.5 and earlier, the Swing system look and feel does not |
| seamlessly support <a href="http://www.microsoft.com/typography/ClearTypeInfo.mspx" target="_new">ClearType</a> |
| anti-aliasing. By default, when ClearType is enabled and the same font and font |
| size are chosen for Swing and SWT controls, the fonts appear to be different. A |
| partial solution is available through setting the |
| property <code class="code">swing.aatext</code> to "true". This undocumented property enables font anti-aliasing |
| in pre-Java 6 environments, but it simply turns anti-aliasing on. It does not synchronize with current |
| Windows system settings. Also, Swing has its own anti-aliasing algorithm, so |
| minor differences from natively-displayed fonts remain visible. |
| </p><p> |
| This problem has been <a href="http://weblogs.java.net/blog/chet/archive/2005/06/phils_font_fixe.html" target="_new">resolved</a> |
| in Java 6 (see bugs <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4726365" target="_new">4726365</a> |
| and <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4871297" target="_new">4871297</a> |
| for more information). |
| </p><p> |
| Below is a screenshot which illustrates the problem. Here, two SWT-based views are on |
| the left, and a Swing table component is on the right. |
| </p><div class="figure"><a name="fig-cleartype"></a><p class="title"><b>Figure 5. Font Differences with ClearType</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/cleartype.jpg" alt="Font Differences with ClearType"></div></div></div><br class="figure-break"></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-cursor-synchronization"></a>Cursor Synchronization</h3></div></div></div><p> |
| SWT cursor changes are not reflected in embedded Swing components. The SWT cursor |
| changes back to the default arrow cursor as the mouse moves over the embedded frame. |
| </p><p> |
| A solution to this problem may now be possible. As of Eclipse 3.3, new API is |
| available to query the current cursor (see bug |
| <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=133943" target="_new">133943</a> |
| ). |
| </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-conclusion"></a>Conclusion</h2></div></div></div><p> |
| Smooth integration of Swing components into an RCP application is possible. It requires |
| more than the SWT/AWT Bridge, however. You can implement the techniques described above |
| in a modular fashion to improve the behavior of the SWT/AWT Bridge for |
| real-world applications. <a href="#app-example-code" title="A. Example Code">Appendix A, <i>Example Code</i></a> contains example |
| code that demonstrates all of these additional practices. |
| </p></div><div class="appendix" lang="en"><h2 class="title" style="clear: both"><a name="app-example-code"></a>A. Example Code</h2><p> |
| The ideas for improving Swing/SWT integration presented in |
| <a href="#sec-adding-to-bridge" title="Building on the SWT/AWT Bridge">the section called “Building on the SWT/AWT Bridge”</a> have been collected into a full |
| example implementation [<a href="#bib-example-code">1</a>]. The example |
| includes an API that is used to create embedded Swing components |
| simply and cleanly. The example code requires SWT version 3.2 or higher. |
| </p><p> |
| Import the example with "File -> Import...", selecting "Plug-ins and Fragments". Import the |
| plug-in as "Projects with Source Folders". Make sure that your |
| compiler settings specify a source compatibility version of at least 1.4 (Preferences -> Java -> Compiler). |
| Otherwise the <code class="code">assert</code> statements in the example code will not compile. |
| </p><p> |
| <a href="files/example-javadoc/index.html" target="_new">Javadoc</a> |
| is available for the API. To embed Swing components, refer to |
| the javadoc for |
| <code class="code"> |
| <a href="files/example-javadoc/swingintegration/example/EmbeddedSwingComposite.html" target="_new"> |
| EmbeddedSwingComposite</a></code>. To display Swing dialogs without embedding, refer to the javadoc for |
| <code class="code"> |
| <a href="files/example-javadoc/swingintegration/example/AwtEnvironment.html" target="_new"> |
| AwtEnvironment</a></code>. |
| </p></div><div class="bibliography"><div class="titlepage"><div><div><h2 class="title"><a name="bin-resources"></a>Resources</h2></div></div></div><div class="biblioentry"><a name="bib-example-code"></a><p>[1] <span class="bibliosource"><a href="files/swingintegration.example_0.0.2.jar" target="_new">Example Code</a>. </span></p></div><div class="biblioentry"><a name="bib-bridge"></a><p>[2] <span class="bibliosource"> |
| <a href="http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/awt/package-summary.html" target="_new"> |
| SWT/AWT Bridge Javadoc |
| </a> |
| . </span></p></div><div class="biblioentry"><a name="bib-look-and-feel"></a><p>[3] <span class="bibliosource"> |
| <a href="http://java.sun.com/docs/books/tutorial/ui/features/plaf.html" target="_new"> |
| Java Look and Feel Information |
| </a> |
| |
| . </span></p></div><div class="biblioentry"><a name="bib-root-pane"></a><p>[4] <span class="bibliosource"> |
| <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JRootPane.html" target="_new"> |
| Swing Root Pane Documentation |
| </a> |
| |
| . </span></p></div><div class="biblioentry"><a name="bib-awt-focus"></a><p>[5] <span class="bibliosource"> |
| <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/doc-files/FocusSpec.html" target="_new">AWT Focus Subsystem</a> |
| . </span></p></div></div><div class="notices"><h3>Legal Notices</h3><p>Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in |
| the United States, other countries, or both.</p><p>Other company, product, or service names may be trademarks or service marks of |
| others.</p></div></div></body></html> |