Reuse your e4 SWT-Part implementation in e4 on JavaFX


Have you ever considered to switch from SWT to JavaFX but had abandoned it because it would require you to rewrite all your SWT UI code or you want to stick with SWT because you want to publish your e4 application on the web using RAP but on the desktop SWT simply doesn’t allow you to theme your UI enough?

If you followed my advise to keep your ui-bundle free from framework dependencies beside:

  • org.eclipse.swt
  • org.eclipse.jface
  • (optionally) org.eclipse.jface.databinding

this is now going to pay off.

Let’s create a little sample step by step

Setup your system

Native SWT application

Native SWT project creation

  • Create an e4 application which only has the Application.e4xmi (no parts, …) – I named mine org.eclipse.fx.sample.singlesource.app.native
  • Create a bare-bone plug-in project which is empty (no activators, no plugin.xml) – I named mine org.eclipse.fx.sample.singlesource.views
  • Add org.eclipse.swtorg.eclipse.jface without version constraints to your plugin

Your workspace should no look like this
native_1

Implement a simple SWT UI

  • Create a package – I named mine org.eclipse.fx.sample.singlesource.views
  • Create a Java class in the above package – i named mine SWTHelloWorld
  • Implement a simple UI – mine looks like this:
    package org.eclipse.fx.sample.singlesource.views;
    
    import javax.annotation.PostConstruct;
    
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.graphics.Font;
    import org.eclipse.swt.graphics.FontData;
    import org.eclipse.swt.layout.GridData;
    import org.eclipse.swt.layout.GridLayout;
    import org.eclipse.swt.widgets.Button;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Event;
    import org.eclipse.swt.widgets.Label;
    
    public class SWTHelloWorld {
    	
    	private Label l;
    
    	@PostConstruct
    	public void initUI(Composite parent) {
    		parent.setLayout(new GridLayout());
    
    		l = new Label(parent, SWT.NONE);
    		l.setAlignment(SWT.CENTER);
    		l.setFont(new Font(Display.getCurrent(), new FontData(l.getFont().getFontData()[0].getName(), 30, SWT.NORMAL)));
    		l.setLayoutData(new GridData(GridData.FILL,GridData.CENTER,true,false));
    		
    		Button b = new Button(parent, SWT.PUSH);
    		b.setText("Hello World");
    		b.addListener(SWT.Selection, this::showHelloWorld);
    		b.setLayoutData(new GridData(GridData.CENTER,GridData.CENTER,true,true));
    	}
    	
    	void showHelloWorld(Event event) {
    		l.setText("SWT on JavaFX is the coolest technology on earth");
    	}
    }
    

Implement a simple JFace viewer

  • Create a Java class next to the SWT-UI one – I named mine JFaceHelloWorld
  • Create a icons-folder in the root of the project
  • Download the famfamfam icon from http://www.famfamfam.com/lab/icons/silk/
  • Copy the following icons to it:
    • folder.png
    • page_white_acrobat.png
    • page_white_cup.png
    • page_white_excel.png
    • page_white_office.png
    • page_white_picture.png
    • page_white_text.png
    • page_white.png
  • The implementation i wrote to test JFace looks like this:
    package org.eclipse.fx.sample.singlesource.views;
    
    import java.io.File;
    
    import javax.annotation.PostConstruct;
    
    import org.eclipse.jface.resource.ImageDescriptor;
    import org.eclipse.jface.resource.JFaceResources;
    import org.eclipse.jface.viewers.ITreeContentProvider;
    import org.eclipse.jface.viewers.LabelProvider;
    import org.eclipse.jface.viewers.TreeViewer;
    import org.eclipse.jface.viewers.Viewer;
    import org.eclipse.swt.graphics.Image;
    import org.eclipse.swt.layout.FillLayout;
    import org.eclipse.swt.widgets.Composite;
    
    public class JFaceHelloWorld {
    	private static final String FOLDER = "FOLDER";
    	private static final String JAVA_FILE = "JAVA_FILE";
    	private static final String PDF_FILE = "PDF_FILE";
    	private static final String EXCEL_FILE = "EXCEL_FILE";
    	private static final String OFFICE_FILE = "OFFICE_FILE";
    	private static final String IMG_FILE = "IMG_FILE";
    	private static final String TEXT_FILE = "TEXT_FILE";
    	private static final String GENERIC_FILE = "GENERIC_FILE";
    	
    	static {
    		JFaceResources.getImageRegistry().put(FOLDER, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/folder.png")));
    		JFaceResources.getImageRegistry().put(JAVA_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_cup.png")));
    		JFaceResources.getImageRegistry().put(PDF_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_acrobat.png")));
    		JFaceResources.getImageRegistry().put(EXCEL_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_excel.png")));
    		JFaceResources.getImageRegistry().put(OFFICE_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_office.png")));
    		JFaceResources.getImageRegistry().put(IMG_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_picture.png")));
    		JFaceResources.getImageRegistry().put(TEXT_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white_text.png")));
    		JFaceResources.getImageRegistry().put(GENERIC_FILE, ImageDescriptor.createFromURL(JFaceHelloWorld.class.getClassLoader().getResource("/icons/page_white.png")));
    	}
    	
    	@PostConstruct
    	public void initUI(Composite parent) {
    		parent.setLayout(new FillLayout());
    		
    		File root = new File(System.getProperty("user.home"));
    		
    		TreeViewer viewer = new TreeViewer(parent);
    		viewer.setContentProvider(new FileSystemContentProvider());
    		viewer.setLabelProvider(new LabelProvider() {
    			@Override
    			public Image getImage(Object element) {
    				File f = (File) element;
    				if( f.isDirectory() ) {
    					return JFaceResources.getImageRegistry().get(FOLDER);
    				} else if( f.getName().endsWith(".java") ) {
    					return JFaceResources.getImageRegistry().get(JAVA_FILE);
    				} else if( f.getName().endsWith(".pdf") ) {
    					return JFaceResources.getImageRegistry().get(PDF_FILE);
    				} else if( f.getName().endsWith(".xls") ) {
    					return JFaceResources.getImageRegistry().get(EXCEL_FILE);
    				} else if( f.getName().endsWith(".doc") ) {
    					return JFaceResources.getImageRegistry().get(OFFICE_FILE);
    				} else if( f.getName().endsWith(".png") || f.getName().endsWith(".jpg") || f.getName().endsWith(".gif") ) {
    					return JFaceResources.getImageRegistry().get(IMG_FILE);
    				} else {
    					return JFaceResources.getImageRegistry().get(GENERIC_FILE);
    				}
    			}
    			
    			@Override
    			public String getText(Object element) {
    				File f = (File) element;
    				if( f == root ) {
    					return f.getAbsolutePath();
    				}
    				return f.getName();
    			}
    		});
    		viewer.setInput(new File[] {root});
    	}
    	
    	static class FileSystemContentProvider implements ITreeContentProvider {
    
    		@Override
    		public void dispose() {
    			
    		}
    
    		@Override
    		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    			
    		}
    
    		@Override
    		public Object[] getElements(Object inputElement) {
    			return (Object[]) inputElement;
    		}
    
    		@Override
    		public Object[] getChildren(Object parentElement) {
    			File f = (File) parentElement;
    			if( f.isDirectory() ) {
    				return f.listFiles();	
    			}
    			return new Object[0];
    		}
    
    		@Override
    		public Object getParent(Object element) {
    			File f = (File) element;
    			return f.getParentFile();
    		}
    
    		@Override
    		public boolean hasChildren(Object element) {
    			return getChildren(element).length > 0;
    		}
    		
    	}
    }
    

Register the parts in your e4xmi & launch

  • Add your org.eclipse.fx.sample.singlesource.views to the apps.native MANIFEST.MF
  • Add your org.eclipse.fx.sample.singlesource.views to the .product-File
  • Create a PartStack & 2 Part elements point to the class above
  • Launch the application using the product-File

You should get an UI like this:
native_2

native_3

Run on e4 on JavaFX and SWT on JavaFX

Setup a target platform

Create an e4 on JavaFX application infrastructure

  • Create new project infrastructure using the e4 Application projects found in the JavaFX/OSGi category
  • use org.eclipse.fx.sample.singlesource as the prefix
  • Finish the dialog with the default settings

You should have a workspace like this
fx_1

Add SWT on JavaFX dependencies

  • Open the feature.xml in org.eclipse.fx.sample.singlesource.app.feature
  • Add the following bundles
    • org.eclipse.fx.runtime.swt
    • org.eclipse.swt
    • org.eclipse.fx.sample.singlesource.views
    • org.eclipse.fx.runtime.swt.e4

Update the e4xmi & Launch

  • Add a dependency to org.eclipse.fx.sample.singlesource.views to the MANIFEST.MF
  • Open the e4xmi and add a PartStack and 2 Part elements pointing to the Java classes we defined
  • Launch the application using the generated Launch Config in the .product project

This will give you a UI like this
fx_3

fx_2

or if you open the default.css in the .app-project and add the following lines

.root {
    -fx-base: rgb(50, 50, 50);
    -fx-background: rgb(50, 50, 50);
    -fx-control-inner-background:  rgb(50, 50, 50);
}

the ui will change to

fx_4

fx_5

You notice that you can exploit the full power of JavaFX CSS immediately!

Finally a video showing the above in full action

This entry was posted in e(fx)clipse. Bookmark the permalink.

11 Responses to Reuse your e4 SWT-Part implementation in e4 on JavaFX

  1. Pingback: JavaFX links of the week, May 12 // JavaFX News, Demos and Insight // FX Experience

  2. Dun says:

    Amazing!
    But what about the jface.databinding plugin?

  3. Dun says:

    Do u have a roadmap for the implementation of the missing classes/methods?

    • Tom Schindl says:

      We don’t have a roadmap for that and made this clear from the beginning if we have time we work on it – if you see something that does not work file a bug and think about sponsored dev and/or fix the problem yourself and provide a gerrit review.

      We simply don’t have the resources to do it for free for the whole world, what you get today already took something between 0.5 and 1 man year.

  4. Harsh Bhavsar says:

    Hey Tom,
    I have tried each and every ways you mentioned, also referred other web sites and blogs too, but not able to embed javaFx in eclipse e4 rcp with swt/jface. So can you provide me basic environment project which has been pre build, so I can do work on that, because I badly want the charts of JavaFx in my application.

    Thank you in Advance

    • Tom Schindl says:

      Please as your question in our forum – I’ve just implemented an easy solution for you so if you post to the forum I’ll provide the relevant informations.

  5. Paul Mira says:

    Hello Tom,

    I’d like to know if the platform definition for this tutorial (the e4+javafx+swtonfx one) and the dependencies added to the feature project will still work as of today. Because I get some missing constraints from org.eclipse.fx.runtime.swt and org.eclipse.runtime.swt.e4. Basically this :

    > org.ecipse.fx.runtime.swt
    > Missing Constraint : Import-Package com.sun.glass.events; version=”2.2.0″
    … (a bunch of similar imports not resolved)

    > org.eclipse.runtime.swt.e4
    > Missing Constraint : Import-Package javafx.scene.layout; version=”2.2.0″

    Thanks in advance

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.