Bug 575763: [UI-Workbench] Add more utils for CSS styling

  - Add stylable VirtualComposite

Change-Id: Ibd8b78949f3f217e00bdd4cc22aa8474358ac257
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF b/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF
index 2082894..e8c39c8 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/META-INF/MANIFEST.MF
@@ -29,6 +29,7 @@
  org.eclipse.debug.ui;resolution:=optional,
  org.eclipse.e4.ui.css.core.dom;resolution:=optional,
  org.eclipse.e4.ui.css.core.engine;resolution:=optional,
+ org.eclipse.e4.ui.css.core.utils;resolution:=optional,
  org.eclipse.e4.ui.css.swt.dom;resolution:=optional,
  org.eclipse.e4.ui.css.swt.properties;resolution:=optional,
  org.eclipse.e4.ui.css.swt.theme;resolution:=optional,
@@ -63,4 +64,6 @@
  org.eclipse.statet.ecommons.ui.viewers,
  org.eclipse.statet.ecommons.ui.viewers.breadcrumb,
  org.eclipse.statet.ecommons.ui.workbench,
+ org.eclipse.statet.ecommons.ui.workbench.css,
+ org.eclipse.statet.ecommons.ui.workbench.css.dom,
  org.eclipse.statet.ecommons.ui.workbench.workspace
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml b/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml
index 16c161c..df85c72 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/plugin.xml
@@ -235,13 +235,15 @@
             class="org.eclipse.statet.internal.ecommons.ui.swt.css.dom.ECommonsSwtElementProvider">
          <widget
                class="org.eclipse.statet.ecommons.ui.swt.expandable.ExpandableComposite"/>
+         <widget
+               class="org.eclipse.statet.internal.ecommons.ui.swt.css.dom.VirtualWidget"/>
       </provider>
    </extension>
    <extension
          point="org.eclipse.e4.ui.css.core.propertyHandler">
       <handler
             adapter="org.eclipse.statet.internal.ecommons.ui.swt.css.dom.ExpandableCompositeElement"
-            handler="org.eclipse.statet.internal.ecommons.ui.swt.css.properties.ExpandableCompositeHandler">
+            handler="org.eclipse.statet.internal.ecommons.ui.swt.css.properties.ExpandableCompositePropertyHandler">
          <property-name
                name="swt-titlebar-color"/>
       </handler>
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java
index af1f424..9e5428f 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/swt/expandable/ExpandableComposite.java
@@ -669,7 +669,7 @@
 						case SWT.Paint:
 							if (ExpandableComposite.this.toggle != null
 									&& (getExpansionStyle() & NO_TITLE_FOCUS_BOX) == 0) {
-								paintTitleFocus(e.gc);
+								paintTitleFocus(nonNullAssert(e.gc));
 							}
 							break;
 						case SWT.Resize:
@@ -1137,7 +1137,8 @@
 		final var layout= (ExpandableLayout)getLayout();
 		if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
 			size= layout.computeSize(this, wHint, hHint, changed);
-		} else {
+		}
+		else {
 			size= new Point(wHint, hHint);
 		}
 		final Rectangle trim= computeTrim(0, 0, size.x, size.y);
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItem.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItem.java
index 1929f76..5c87674 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItem.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItem.java
@@ -29,7 +29,7 @@
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
 
-import org.eclipse.statet.ecommons.ui.workbench.StylingUtils;
+import org.eclipse.statet.ecommons.ui.workbench.css.StylingUtils;
 
 
 /**
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDetails.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDetails.java
index 8503fe6..c05049f 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDetails.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDetails.java
@@ -43,7 +43,7 @@
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
 
-import org.eclipse.statet.ecommons.ui.workbench.StylingUtils;
+import org.eclipse.statet.ecommons.ui.workbench.css.StylingUtils;
 
 
 /**
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java
index aff4838..0e4359c 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java
@@ -68,7 +68,7 @@
 import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.ui.swt.AccessibleArrowImage;
-import org.eclipse.statet.ecommons.ui.workbench.StylingUtils;
+import org.eclipse.statet.ecommons.ui.workbench.css.StylingUtils;
 
 
 /**
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbViewer.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbViewer.java
index bf5beae..f32afed 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbViewer.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/viewers/breadcrumb/BreadcrumbViewer.java
@@ -56,7 +56,7 @@
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
 
-import org.eclipse.statet.ecommons.ui.workbench.StylingUtils;
+import org.eclipse.statet.ecommons.ui.workbench.css.StylingUtils;
 
 
 /**
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/StylingUtils.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/StylingUtils.java
similarity index 98%
rename from ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/StylingUtils.java
rename to ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/StylingUtils.java
index 784fdc7..6ff6d29 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/StylingUtils.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/StylingUtils.java
@@ -12,7 +12,7 @@
  #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
  #=============================================================================*/
 
-package org.eclipse.statet.ecommons.ui.workbench;
+package org.eclipse.statet.ecommons.ui.workbench.css;
 
 import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
 
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/VirtualComposite.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/VirtualComposite.java
new file mode 100644
index 0000000..90b4c4a
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/VirtualComposite.java
@@ -0,0 +1,23 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.ui.workbench.css;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public interface VirtualComposite {
+	
+}
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualCompositeStylableElement.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualCompositeStylableElement.java
new file mode 100644
index 0000000..334eefc
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualCompositeStylableElement.java
@@ -0,0 +1,98 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.ui.workbench.css.dom;
+
+import org.eclipse.e4.ui.css.core.engine.CSSEngine;
+import org.eclipse.e4.ui.css.swt.dom.WidgetElement;
+import org.eclipse.swt.widgets.Control;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.workbench.css.VirtualComposite;
+import org.eclipse.statet.internal.ecommons.ui.swt.css.dom.VirtualCompositeSkinListener;
+
+
+@NonNullByDefault
+@SuppressWarnings("restriction")
+public abstract class VirtualCompositeStylableElement<TControl extends Control & VirtualComposite>
+		extends WidgetElement implements NodeList {
+	
+	
+	private final ImList<? extends VirtualStylableElement<? extends VirtualCompositeStylableElement<TControl>>> children;
+	
+	
+	public VirtualCompositeStylableElement(final TControl control, final CSSEngine engine) {
+		super(control, engine);
+		
+		this.children= createChildren(control);
+		
+		control.addDisposeListener((event) -> dispose());
+		
+		VirtualCompositeSkinListener.enable(control.getDisplay(), engine);
+	}
+	
+	protected abstract ImList<? extends VirtualStylableElement<? extends VirtualCompositeStylableElement<TControl>>> createChildren(
+			final TControl control);
+	
+	@Override
+	public void dispose() {
+		super.dispose();
+		
+		for (final var child : this.children) {
+			this.engine.handleWidgetDisposed(child);
+			child.dispose();
+		}
+	}
+	
+	
+	@Override
+	public NodeList getChildNodes() {
+		return this;
+	}
+	
+	@Override
+	public int getLength() {
+		return this.children.size();
+	}
+	
+	@Override
+	public @Nullable Node item(final int index) {
+		if (index >= 0 && index < this.children.size()) {
+			return this.children.get(index);
+		}
+		return null;
+	}
+	
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public TControl getNativeWidget() {
+		return (TControl)super.getNativeWidget();
+	}
+	
+	
+	@Override
+	public String toString() {
+		final var sb= new StringBuilder();
+		VirtualStylableElement.appendName(sb, this, true);
+		return sb.toString();
+	}
+	
+}
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualStylableElement.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualStylableElement.java
new file mode 100644
index 0000000..991dd94
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/workbench/css/dom/VirtualStylableElement.java
@@ -0,0 +1,156 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.ui.workbench.css.dom;
+
+import org.eclipse.e4.ui.css.core.dom.CSSStylableElement;
+import org.eclipse.e4.ui.css.core.dom.ElementAdapter;
+import org.eclipse.e4.ui.css.core.engine.CSSEngine;
+import org.eclipse.e4.ui.css.core.utils.ClassUtils;
+
+import org.w3c.dom.NodeList;
+
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.internal.ecommons.ui.swt.css.dom.VirtualWidget;
+
+
+@NonNullByDefault
+@SuppressWarnings("restriction")
+public abstract class VirtualStylableElement<TParent extends CSSStylableElement>
+		extends ElementAdapter implements NodeList {
+	
+	
+	private final TParent parent;
+	
+	private final String name;
+	private final @Nullable String cssClass;
+	
+	private ImList<? extends VirtualStylableElement<? extends VirtualStylableElement<?>>> children= ImCollections.emptyList();
+	
+	
+	public VirtualStylableElement(final TParent parent,
+			final String name, final @Nullable String cssClass,
+			final CSSEngine engine) {
+		super(new VirtualWidget(), engine);
+		((VirtualWidget)getNativeWidget()).setElement(this);
+		this.parent= parent;
+		this.name= name;
+		this.cssClass= cssClass;
+	}
+	
+	protected void init(final ImList<? extends VirtualStylableElement<? extends VirtualStylableElement<TParent>>> children) {
+		this.children= children;
+	}
+	
+	@Override
+	public void dispose() {
+		super.dispose();
+		
+		for (final VirtualStylableElement<?> child : this.children) {
+			child.dispose();
+		}
+	}
+	
+	
+	@Override
+	public TParent getParentNode() {
+		return this.parent;
+	}
+	
+	@Override
+	public NodeList getChildNodes() {
+		return this;
+	}
+	
+	@Override
+	public int getLength() {
+		return this.children.size();
+	}
+	
+	@Override
+	public @Nullable VirtualStylableElement<? extends VirtualStylableElement<?>> item(final int index) {
+		if (index >= 0 && index < this.children.size()) {
+			return this.children.get(index);
+		}
+		return null;
+	}
+	
+	
+	@Override
+	public @Nullable String getNamespaceURI() {
+		return ClassUtils.getPackageName(VirtualCompositeStylableElement.class);
+	}
+	
+	@Override
+	public String getLocalName() {
+		return this.name;
+	}
+	
+	@Override
+	public @Nullable String getCSSId() {
+		return null;
+	}
+	
+	@Override
+	public @Nullable String getCSSClass() {
+		return this.cssClass;
+	}
+	
+	@Override
+	public @Nullable String getCSSStyle() {
+		return null;
+	}
+	
+	@Override
+	public String getAttribute(final String name) {
+		return ""; //$NON-NLS-1$
+	}
+	
+	
+	@Override
+	public String toString() {
+		final var sb= new StringBuilder();
+		appendNameRec(sb, this);
+		return sb.toString();
+	}
+	
+	static void appendNameRec(final StringBuilder sb, final VirtualStylableElement<?> element) {
+		final var parentNode= element.getParentNode();
+		if (parentNode instanceof VirtualStylableElement<?>) {
+			appendNameRec(sb, (VirtualStylableElement<?>)parentNode);
+			sb.append(' ');
+		}
+		else {
+			appendName(sb, parentNode, true);
+			sb.append(' ');
+		}
+		appendName(sb, element, false);
+	}
+	
+	static void appendName(final StringBuilder sb, final CSSStylableElement element, final boolean id) {
+		sb.append(element.getLocalName());
+		final String cssClass= element.getCSSClass();
+		if (cssClass != null) {
+			sb.append('.').append(cssClass);
+		}
+		if (id) {
+			sb.append('#').append(System.identityHashCode(element));
+		}
+	}
+	
+}
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java
index a3528b5..f624820 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/ECommonsSwtElementProvider.java
@@ -36,6 +36,9 @@
 		if (element instanceof ExpandableComposite) {
 			return new ExpandableCompositeElement((ExpandableComposite)element, engine);
 		}
+		if (element instanceof VirtualWidget) {
+			return ((VirtualWidget)element).getElement();
+		}
 		return null;
 	}
 	
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualCompositeSkinListener.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualCompositeSkinListener.java
new file mode 100644
index 0000000..5551902
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualCompositeSkinListener.java
@@ -0,0 +1,63 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.ecommons.ui.swt.css.dom;
+
+import org.eclipse.e4.ui.css.core.engine.CSSEngine;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.ui.workbench.css.VirtualComposite;
+import org.eclipse.statet.ecommons.ui.workbench.css.dom.VirtualStylableElement;
+
+
+/**
+ * Add a listener for {@link SWT#Skin} to apply styles to {@link VirtualStylableElement}s of
+ * {@link VirtualComposite}s.
+ */
+@NonNullByDefault
+@SuppressWarnings("restriction")
+public class VirtualCompositeSkinListener implements Listener {
+	
+	
+	private static boolean isEnabled;
+	
+	public static void enable(final Display display, final CSSEngine engine) {
+		if (isEnabled) {
+			return;
+		}
+		display.addListener(SWT.Skin, new VirtualCompositeSkinListener(display, engine));
+		isEnabled= true;
+	}
+	
+	
+	private final CSSEngine engine;
+	
+	
+	public VirtualCompositeSkinListener(final Display display, final CSSEngine cssEngine) {
+		this.engine = cssEngine;
+	}
+	
+	@Override
+	public void handleEvent(final Event event) {
+		if (event.widget instanceof VirtualComposite) {
+			VirtualCompositeSkinListener.this.engine.applyStyles(event.widget, true);
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualWidget.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualWidget.java
new file mode 100644
index 0000000..5f87be7
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/dom/VirtualWidget.java
@@ -0,0 +1,43 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.ecommons.ui.swt.css.dom;
+
+import org.eclipse.e4.ui.css.core.dom.CSSStylableElement;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+@SuppressWarnings("restriction")
+public class VirtualWidget {
+	
+	
+	private CSSStylableElement element;
+	
+	
+	@SuppressWarnings("null")
+	public VirtualWidget() {
+	}
+	
+	public void setElement(final CSSStylableElement element) {
+		this.element= element;
+	}
+	
+	
+	public CSSStylableElement getElement() {
+		return this.element;
+	}
+	
+}
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositePropertyHandler.java
similarity index 70%
rename from ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java
rename to ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositePropertyHandler.java
index 7d749d0..1db3d97 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositeHandler.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/internal/ecommons/ui/swt/css/properties/ExpandableCompositePropertyHandler.java
@@ -29,7 +29,7 @@
 
 @NonNullByDefault
 @SuppressWarnings("restriction")
-public class ExpandableCompositeHandler extends AbstractCSSPropertySWTHandler {
+public class ExpandableCompositePropertyHandler extends AbstractCSSPropertySWTHandler {
 	
 	
 	private static final String TITLE_BAR_FOREGROUND= "swt-titlebar-color"; //$NON-NLS-1$
@@ -38,6 +38,31 @@
 	@Override
 	protected @Nullable String retrieveCSSProperty(final Control control, final String property,
 			final @Nullable String pseudo, final CSSEngine engine) throws Exception {
+		if (!(control instanceof ExpandableComposite)) {
+			return null;
+		}
+		final var expandableComposite= (ExpandableComposite)control;
+		
+		switch (property) {
+		case TITLE_BAR_FOREGROUND:
+			if (pseudo == null) {
+				return engine.convert(expandableComposite.getTitleBarForeground(),
+						Color.class, null );
+			}
+			return null;
+//		case CSSPropertyFormHandler.TB_TOGGLE:
+//			if (pseudo == null) {
+//				expandableComposite.setToggleColor(
+//						(Color)engine.convert(value, Color.class, control.getDisplay()) );
+//			}
+//			else if (pseudo.equalsIgnoreCase("hover")) { //$NON-NLS-1$
+//				expandableComposite.setToggleHoverColor(
+//						(Color)engine.convert(value, Color.class, control.getDisplay()) );
+//			}
+//			return;
+		default:
+			break;
+		}
 		return null;
 	}
 	
@@ -45,16 +70,15 @@
 	protected void applyCSSProperty(final Control control, final String property,
 			final CSSValue value,
 			final @Nullable String pseudo, final CSSEngine engine) throws Exception {
-		if (!(control instanceof ExpandableComposite)
-				|| property == null
-				|| value.getCssValueType() != CSSValue.CSS_PRIMITIVE_VALUE) {
+		if (!(control instanceof ExpandableComposite)) {
 			return;
 		}
 		final var expandableComposite= (ExpandableComposite)control;
 		
-		switch (property.toLowerCase()) {
+		switch (property) {
 		case TITLE_BAR_FOREGROUND:
-			if (pseudo == null) {
+			if (pseudo == null
+					&& value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
 				expandableComposite.setTitleBarForeground(
 						(Color)engine.convert(value, Color.class, control.getDisplay()) );
 			}
@@ -70,8 +94,9 @@
 //			}
 //			return;
 		default:
-			return;
+			break;
 		}
+		return;
 	}
 	
 }