Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Standard Widget Toolkit (SWT) » Trying to theme SWT (CSS?)(Want to create a dark mode look for my SWT application)
Trying to theme SWT (CSS?) [message #1821946] Mon, 24 February 2020 10:46 Go to next message
Stuart Ledwich is currently offline Stuart LedwichFriend
Messages: 10
Registered: November 2010
Junior Member
Hi all,

I have a standalone SWT app that I would like to create a dark theme for, I can see there is some CSS support for GTK (org.eclipse.swt.internal.gtk.css) but I cant see anyway to implement using Windows. I also see that eclipse on Windows appears to support themes so was just wondering if there is anything that anybody can point me to, to help create a windows theme?

Any help/advice welcome. Thank you

Re: Trying to theme SWT (CSS?) [message #1822100 is a reply to message #1821946] Thu, 27 February 2020 09:42 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
I also need standalone swt css styling.

I fetched some jars from eclipse plugin directory and tried with CSSSWTEngineImpl class but no luck.

I know that usually this is used with e4 RCP.
But I'd like to use this function with standalone SWT/JFace app.

The following is the code I tried. (usual black label appears)
import java.io.IOException;
import java.io.StringReader;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.core.runtime.spi.IRegistryProvider;
import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

public class _CSSTest {
	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell();
		shell.setLayout(new GridLayout());
		
		try {
			RegistryFactory.setDefaultRegistryProvider(new IRegistryProvider() {
				final IExtensionRegistry r = RegistryFactory.createRegistry(null, null, null);
				@Override
				public IExtensionRegistry getRegistry() {
					return r;
				}
			});
		} catch (CoreException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		 
		Label label = new Label(shell, SWT.LEAD);
		label.setText("Hello world!");
		label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		 
		CSSEngine engine = new CSSSWTEngineImpl(display);
		try {
			engine.parseStyleSheet(new StringReader("Label { color: blue; }"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		engine.setErrorHandler(new CSSErrorHandler() {
		  public void error(Exception e) {
		    e.printStackTrace();
		  }
		});
		// applying styles to the child nodes means that the engine
		// should recurse downwards, in this example, the engine
		// should style the children of the Shell
		engine.applyStyles(shell, /* applyStylesToChildNodes */ true);
		 
		shell.setSize(400, 300);
		shell.open();
		 
		while (!shell.isDisposed()) {
		  if (!display.readAndDispatch()) {
		    display.sleep();
		  }
		}
		display.dispose();
	}
}

[Updated on: Thu, 27 February 2020 09:42]

Report message to a moderator

Re: Trying to theme SWT (CSS?) [message #1822114 is a reply to message #1822100] Thu, 27 February 2020 14:06 Go to previous messageGo to next message
Stuart Ledwich is currently offline Stuart LedwichFriend
Messages: 10
Registered: November 2010
Junior Member
Thanks for your reply,

I think that looking at the SWT code the only way is going to be to set the colour on every composite directly which is real pain but I cant see a way around it.
Re: Trying to theme SWT (CSS?) [message #1822125 is a reply to message #1822114] Thu, 27 February 2020 16:04 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
Now it seems working.
I had to manually set ElementProvider and PropertyHandler.

This page might have some information about handlers.
https://github.com/eclipse/eclipse.platform.ui/blob/master/bundles/org.eclipse.e4.ui.css.swt/plugin.xml

The following code only works for background-color property.

Jars I'm using:
SWT
swt-win64.jar

JFace
com.ibm.icu_64.2.0.v20190507-1337.jar
org.eclipse.core.commands_3.9.400.v20190516-1358.jar
org.eclipse.core.jobs_3.10.400.v20190506-1457.jar
org.eclipse.core.runtime_3.15.300.v20190508-0543.jar
org.eclipse.equinox.common_3.10.400.v20190516-1504.jar
org.eclipse.equinox.registry_3.8.400.v20190516-1504.jar
org.eclipse.jface_3.16.0.v20190528-0922.jar
org.eclipse.jface.databinding_1.9.0.v20190519-0933.jar
org.eclipse.jface.text_3.15.200.v20190519-2344.jar
org.eclipse.osgi_3.14.0.v20190517-1309.jar
org.eclipse.text_3.8.200.v20190519-2344.jar
org.eclipse.ui.forms_3.8.0.v20190519-1034.jar
org.eclipse.ui.workbench_3.115.0.v20190521-1602.jar


SWT CSS
org.apache.batik.css_1.11.0.v20190515-0436.jar
org.apache.batik.i18n_1.11.0.v20190515-0436.jar
org.apache.batik.util_1.11.0.v20190515-0436.jar
org.eclipse.e4.ui.css.core_0.12.900.v20191106-1716.jar
org.eclipse.e4.ui.css.swt_0.13.700.v20191113-1031.jar
org.eclipse.e4.ui.css.swt.theme_0.12.500.v20191125-1011.jar
org.w3c.css.sac_1.3.1.v200903091627.jar



import java.io.IOException;
import java.io.StringReader;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.core.runtime.spi.IRegistryProvider;
import org.eclipse.e4.ui.css.core.dom.properties.css2.ICSSPropertyBackgroundHandler;
import org.eclipse.e4.ui.css.core.dom.properties.providers.CSSPropertyHandlerSimpleProviderImpl;
import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
import org.eclipse.e4.ui.css.swt.dom.SWTElementProvider;
import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
import org.eclipse.e4.ui.css.swt.properties.css2.CSSPropertyBackgroundSWTHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

public class _CSSTest {
	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell();
		shell.setLayout(new GridLayout());
		
		try {
			RegistryFactory.setDefaultRegistryProvider(new IRegistryProvider() {
				final IExtensionRegistry r = RegistryFactory.createRegistry(null, null, null);
				@Override
				public IExtensionRegistry getRegistry() {
					return r;
				}
			});
		} catch (CoreException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		 
		Label label = new Label(shell, SWT.LEAD);
		label.setText("Hello world!");
		label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
		 
		var engine = new CSSSWTEngineImpl(display);
		engine.setElementProvider(new SWTElementProvider());
		engine.registerCSSPropertyHandlerProvider(new CSSPropertyHandlerSimpleProviderImpl());
		engine.registerCSSPropertyHandler(ICSSPropertyBackgroundHandler.class, CSSPropertyBackgroundSWTHandler.INSTANCE);
		
		try {
			engine.parseStyleSheet(new StringReader("Label { background-color: blue; }"));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		engine.setErrorHandler(new CSSErrorHandler() {
		  public void error(Exception e) {
		    e.printStackTrace();
		  }
		});
		// applying styles to the child nodes means that the engine
		// should recurse downwards, in this example, the engine
		// should style the children of the Shell
		engine.applyStyles(label, /* applyStylesToChildNodes */ true);
		 
		shell.setSize(400, 300);
		shell.open();
		 
		while (!shell.isDisposed()) {
		  if (!display.readAndDispatch()) {
		    display.sleep();
		  }
		}
		display.dispose();
	}
}
Re: Trying to theme SWT (CSS?) [message #1822152 is a reply to message #1822125] Fri, 28 February 2020 03:41 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
I struggled a bit, and finally made a working example.

This example downloads plugin.xml from GITHUB, and parse it.
So what you need is just SWT / JFace / e4 CSS Jars in your classpath.

import java.util.function.BiFunction;
import java.util.function.Function;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

import org.eclipse.core.internal.registry.ExtensionRegistry;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.core.runtime.spi.RegistryContributor;
import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
import org.eclipse.e4.ui.css.core.impl.engine.RegistryCSSPropertyHandlerProvider;
import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;

public class _SWTStandaloneCSS {
	public static void main(String[] args) {		
		var registry = new ExtensionRegistry(null, null, null);
		var dummyContributor = new RegistryContributor("", "", "", "");
		
		registry.addExtensionPoint("org.eclipse.e4.ui.css.core.elementProvider", dummyContributor, true, null, null, null);
		registry.addExtensionPoint("org.eclipse.e4.ui.css.core.propertyHandler", dummyContributor, true, null, null, null);
		
		try {
			registry.addContribution(pluginXMLStream(), dummyContributor, true, null, null, null);
			RegistryFactory.setDefaultRegistryProvider(() -> registry);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		SWTBoilerplate.show(composite -> {
			var engine = new CSSSWTEngineImpl(composite.getDisplay());
			
			initCSSEngine(engine, registry);
			
			var content = createContent(composite);
			
			try {
				engine.parseStyleSheet(new StringReader("Label { color : red; background-color: blue; }"));
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			
			engine.applyStyles(composite.getShell(), /* applyStylesToChildNodes */ true);
			
			return content;
		});
	}
	
	static FileInputStream pluginXMLStream() { 
		File f = download("https://raw.githubusercontent.com/eclipse/eclipse.platform.ui/master/bundles/org.eclipse.e4.ui.css.swt/plugin.xml", "swt_css_plugin.xml");
		try {
			return new FileInputStream(f);
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}
	
	static File download(String url, String destFilePath) {
		var f = new File(destFilePath);
		if( f.exists() ) return f;
		
		URL website;
		try {
			website = new URL(url);
			ReadableByteChannel rbc = Channels.newChannel(website.openStream());
			FileOutputStream fos = new FileOutputStream(destFilePath);
			fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
			fos.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		return f;
     }
	
	static void initCSSEngine(CSSSWTEngineImpl engine, ExtensionRegistry registry) {
		engine.registerCSSPropertyHandlerProvider(new RegistryCSSPropertyHandlerProvider(registry));
		engine.setErrorHandler(new CSSErrorHandler() {
			public void error(Exception e) {
				e.printStackTrace();
			}
		});
	}

	static Control createContent(Composite comp) {
		Label label = new Label(comp, SWT.LEAD);
		label.setText("Hello world!");

		return label;
	}
}

class SWTBoilerplate {
	private final Display display;
	private Shell   shell;
	
	public SWTBoilerplate() {
		if( Display.getCurrent() == null) {
			display = new Display();	
		} else {
			display = Display.getCurrent();
		}
	}
	
	public static void show(Image image) {
		SWTBoilerplate app = new SWTBoilerplate();
		var com = app.launch();
		Label l = new Label(com, SWT.NONE);
		l.setImage(image);
		app.loop(true);
	}
	
	public static void show(Function<Composite, Control> creator) {
		SWTBoilerplate app = new SWTBoilerplate();
		var com = app.launch();
		creator.apply(com);
		app.loop(true);
	}
	
	public static void show(BiFunction<Composite, Shell, Control> creator) {
		SWTBoilerplate app = new SWTBoilerplate();
		var com = app.launch();
		creator.apply(com, com.getShell());
		app.loop(true);
	}
	
	public Composite launch() {
		return launch(new FillLayout(), SWT.SHELL_TRIM);
	}
	
	public Composite launch( Layout layout , int shellStyle) {
		shell = new Shell(display, shellStyle);
		
		shell.setLayout(new FillLayout());
		shell.setSize(800,600);
		Composite comp = new Composite(shell, SWT.None);
		comp.setLayout( layout );
		
		return comp;
	}
	
	public void loop() {
	    loop(false);
	}
	
	public void loop(boolean pack) {
		if( shell == null ) {
			shell = Display.getCurrent().getShells()[0];
		}
		
		shell.open();
		if(pack) shell.pack();
		while( !shell.isDisposed() ) {
			if( !display.readAndDispatch() ) {
				display.sleep();
			}
		}
		display.dispose();
	}
	
	public static void nloop() {
		new SWTBoilerplate().loop();
	}
}

[Updated on: Fri, 28 February 2020 03:47]

Report message to a moderator

Re: Trying to theme SWT (CSS?) [message #1822156 is a reply to message #1822152] Fri, 28 February 2020 07:50 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
And you can switch entire theme by fetching css file from web.

Maybe, also you can use "org.eclipse.e4.ui.css.swt.theme" extension point.
But the following is adequate for standalone SWT application in my case.

//engine.parseStyleSheet(new StringReader("Label { color : red; background-color: blue; }"));
				
File f = download("https://raw.githubusercontent.com/guari/eclipse-ui-theme/master/com.github.eclipseuitheme.themes.plugin/themes/css/moonrise-ui-standalone.css", 
						"moonrise-ui-standalone.css");
engine.parseStyleSheet(new FileInputStream(f));
Re: Trying to theme SWT (CSS?) [message #1822158 is a reply to message #1821946] Fri, 28 February 2020 09:23 Go to previous messageGo to next message
Stuart Ledwich is currently offline Stuart LedwichFriend
Messages: 10
Registered: November 2010
Junior Member
Great! thank you so much for the help, really appreciated. I will give these a try today. Thanks again
Re: Trying to theme SWT (CSS?) [message #1822161 is a reply to message #1822158] Fri, 28 February 2020 10:11 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
Further, I tested in my SWT app.
But unfortunately some renderers seems to be tied with platform bundle system.
Some advanced Controls such as CTabFolders, produce NPE (because they can't find the bundle).

It seems we need some more tweak to perfectly work with the SWT Standalone Application.
Or we have to try e4 RCP Application instead.

Let me know if you have a better solution.
Thank you.

[Updated on: Fri, 28 February 2020 10:15]

Report message to a moderator

Re: Trying to theme SWT (CSS?) [message #1822167 is a reply to message #1822161] Fri, 28 February 2020 11:18 Go to previous messageGo to next message
Stuart Ledwich is currently offline Stuart LedwichFriend
Messages: 10
Registered: November 2010
Junior Member
Missing name Mising name wrote on Fri, 28 February 2020 05:11
Further, I tested in my SWT app.
But unfortunately some renderers seems to be tied with platform bundle system.
Some advanced Controls such as CTabFolders, produce NPE (because they can't find the bundle).

It seems we need some more tweak to perfectly work with the SWT Standalone Application.
Or we have to try e4 RCP Application instead.

Let me know if you have a better solution.
Thank you.


Ok thanks - i will let you know if I find anything.
Re: Trying to theme SWT (CSS?) [message #1822178 is a reply to message #1822167] Fri, 28 February 2020 19:53 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
Now I could handle with the NPE of CSSPropertyTabRendererSWTHandler.

Just rewrite CSSPropertyTabRendererSWTHandler in your code.
Replace the corresponding class name in plugin.xml and load this instead of downloading from GITHUB at runtime.

I searched in "moonrise-ui-standalone.css" and the only part which uses bundle system was CTabFolderRenderer.
So the live issue was handled.

import java.lang.reflect.Constructor;

import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.css.swt.helpers.URI;
import org.eclipse.e4.ui.css.swt.properties.AbstractCSSPropertySWTHandler;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabFolderRenderer;
import org.eclipse.swt.widgets.Control;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSValue;

public class StandaloneCSSPropertyTabRendererSWTHandler extends AbstractCSSPropertySWTHandler {

	@Override
	protected void applyCSSProperty(Control control, String property, CSSValue value, String pseudo, CSSEngine engine)
			throws Exception {
		if (!(control instanceof CTabFolder)) {
			return;
		}
		if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
			if (((CSSPrimitiveValue) value).getPrimitiveType() == CSSPrimitiveValue.CSS_URI) {
				String rendURL = ((CSSPrimitiveValue) value).getStringValue();
				URI uri = URI.createURI(rendURL);
				
				if (uri.authority().equals("org.eclipse.e4.ui.workbench.renderers.swt")) {
					Class<?> targetClass = org.eclipse.e4.ui.workbench.renderers.swt.CTabRendering.class;
					// check to see if the folder already has an instance of the same renderer

					CTabFolderRenderer renderer = ((CTabFolder) control).getRenderer();
					if (renderer != null && renderer.getClass() == targetClass) {
						return;
					}
					Constructor<?> constructor = targetClass.getConstructor(CTabFolder.class);
					if (constructor != null) {
						Object rend = constructor.newInstance(control);
						if (rend != null && rend instanceof CTabFolderRenderer) {
							((CTabFolder) control).setRenderer((CTabFolderRenderer) rend);
						}
					}
//				}
				} else {
					((CTabFolder) control).setRenderer(null);
				}
			}
		}
	}

	@Override
	protected String retrieveCSSProperty(Control control, String property, String pseudo, CSSEngine engine)
			throws Exception {
		return null;
	}
}

[Updated on: Sat, 29 February 2020 02:49]

Report message to a moderator

Re: Trying to theme SWT (CSS?) [message #1822180 is a reply to message #1822178] Fri, 28 February 2020 20:31 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
You can set classname and id for the widget.
This is very useful.

	public static void setClassName(Widget w, String className) {
		w.setData("org.eclipse.e4.ui.css.CssClassName", className);
	}
	
	public static void setId(Widget w, String id) {
		w.setData("org.eclipse.e4.ui.css.id", id);
	}
Re: Trying to theme SWT (CSS?) [message #1822184 is a reply to message #1822180] Sat, 29 February 2020 02:48 Go to previous messageGo to next message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
It seems I wrote the wrong TabFolder rendering class above.
The following is the correct one. [already edited]

Class<?> targetClass = org.eclipse.e4.ui.workbench.renderers.swt.CTabRendering.class;

[Updated on: Sat, 29 February 2020 02:50]

Report message to a moderator

Re: Trying to theme SWT (CSS?) [message #1822208 is a reply to message #1822184] Sun, 01 March 2020 00:47 Go to previous message
Missing name Mising name is currently offline Missing name Mising nameFriend
Messages: 43
Registered: July 2011
Member
If you extends Composite or Shell, simple class name is used as Element name.
Composite or Shell selectors are not applied.

CustomWidget {
background-color: red;
}


If you want a custom property, perhaps standard way in RCP is using extension points.
But you can find properties programmatically easily, no need to use extension points and no need to modify plugin.xml one by one.

public static String property(Widget w, String property) {
	var engine = getEngine(w.getDisplay());
	var element = engine.getElement(w);
	var style = engine.getViewCSS().getComputedStyle(element, null);
		
	if( style == null ) {
		System.out.println("style is null : " + element);
		return null;
	}
		
	var cssVal = style.getPropertyCSSValue(property);
	
	return cssVal != null ? cssVal.getCssText() : null;
}

Previous Topic:Standalone SWT with Central Maven repo
Next Topic:Composite background & dark theme
Goto Forum:
  


Current Time: Sat Apr 17 08:50:52 GMT 2021

Powered by FUDForum. Page generated in 0.01881 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top