blob: 42d8b19c36e2a2fb20ca52437409eb903446cde8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 Angelo Zerr and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.ui.css.swt.dom;
import java.util.Objects;
import java.util.function.Supplier;
import org.eclipse.core.runtime.Assert;
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.eclipse.e4.ui.css.swt.CSSSWTConstants;
import org.eclipse.e4.ui.css.swt.helpers.SWTStyleHelpers;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Widget;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* {@link CSSStylableElement} implementation which wrap SWT {@link Widget}.
*
*/
public class WidgetElement extends ElementAdapter implements NodeList {
boolean dynamicEnabled = Boolean.getBoolean("org.eclipse.e4.ui.css.dynamic");
/**
* Convenience method for getting the CSS class of a widget.
*
* @param widget
* SWT widget with associated CSS class name
* @return CSS class name
*/
public static String getCSSClass(Widget widget) {
return (String) widget.getData(CSSSWTConstants.CSS_CLASS_NAME_KEY);
}
/**
* Convenience method for getting the CSS ID of a widget.
*
* @param widget
* SWT widget with associated CSS id
* @return CSS ID
*/
public static String getID(Widget widget) {
return (String) widget.getData(CSSSWTConstants.CSS_ID_KEY);
}
/**
* Convenience method for setting the CSS class of a widget.
*
* @param widget
* SWT widget with associated CSS class name
* @param className
* class name to set
*/
public static void setCSSClass(Widget widget, String className) {
widget.setData(CSSSWTConstants.CSS_CLASS_NAME_KEY, className);
}
/**
* Convenience method for setting the CSS ID of a widget.
*
* @param widget
* SWT widget with associated CSS id
* @param id
* CSS id to set
*/
public static void setID(Widget widget, String id) {
widget.setData(CSSSWTConstants.CSS_ID_KEY, id);
}
/**
* Convenience method for getting the CSS engine responsible for a widget.
* @param widget SWT widget which is styled by an engine
*/
public static CSSEngine getEngine(Widget widget) {
return getEngine(widget.getDisplay());
}
/**
* Convenience method for getting the CSS engine responsible for a widget.
*
* @param display
* SWT display which is styled by an engine
*/
public static CSSEngine getEngine(Display display) {
return (CSSEngine) display.getData(CSSSWTConstants.CSS_ENGINE_KEY);
}
/**
* Convenience method for requesting the CSS engine to re-apply styles to a
* widget.
*
* @param widget
* widget to be restyled
* @param applyStylesToChildNodes
* if true, apply styles to the child nodes
*/
public static void applyStyles(Widget widget,
boolean applyStylesToChildNodes) {
CSSEngine engine = getEngine(widget);
if (engine != null) {
engine.applyStyles(widget, applyStylesToChildNodes);
}
}
/**
* Convenience method for setting the CSS engine responsible for a display.
*
* @param widget
* SWT display which is styled by an engine
* @param engine
* Engine to be associated with the display
*/
public static void setEngine(Display display, CSSEngine engine) {
display.setData(CSSSWTConstants.CSS_ENGINE_KEY, engine);
}
protected String localName;
protected String namespaceURI;
protected String swtStyles;
public WidgetElement(Widget widget, CSSEngine engine) {
super(widget, engine);
this.localName = computeLocalName();
this.namespaceURI = computeNamespaceURI();
this.computeStaticPseudoInstances();
this.swtStyles = this.computeAttributeSWTStyle();
}
/**
* Compute local name.
*
* @return
*/
protected String computeLocalName() {
// The localName is simple class name
// of the SWT widget. For instance
// for the org.eclipse.swt.widgets.Label
// localName is Label
// CSS selector will use this localName
// ex : Label {background-color:red;}
// for inner classes, the hyphen is used, e.g., for Outer$Inner,
// the selector is Outer-Inner {background-color:red;}
Widget widget = getWidget();
Class<?> clazz = widget.getClass();
return ClassUtils.getSimpleName(clazz);
}
/**
* Compute namespaceURI.
*
* @return
*/
protected String computeNamespaceURI() {
// The NamespaceURI is package name
// of the SWT widget. For instance
// for the org.eclipse.swt.widgets.Label
// namespaceURI is org.eclipse.swt.widgets.Label
// CSS selector will use this localName
// @namespace eclipse org.eclipse.swt.widgets.Label
// ex : eclipse|Label {background-color:red;}
Widget widget = getWidget();
Class<?> clazz = widget.getClass();
return ClassUtils.getPackageName(clazz);
}
/**
* Compute static pseudo instances.
*
*/
protected void computeStaticPseudoInstances() {
}
/**
* Compute attribute SWT style.
*
* @return
*/
protected String computeAttributeSWTStyle() {
return SWTStyleHelpers.getSWTWidgetStyleAsString(getWidget());
}
@Override
public final String getAttribute(String attr) {
Supplier<String> attribute = internalGetAttribute(attr);
if (attribute != null) {
String attributeValue = attribute.get();
Assert.isNotNull(attributeValue);
return attributeValue;
}
return "";
}
@Override
public final boolean hasAttribute(String attr) {
return internalGetAttribute(attr) != null;
}
/**
*
* The goal for {@link #internalGetAttribute(String)} is to share the code
* of {@link #hasAttribute(String)} and {@link #getAttribute(String)} and to
* keep the performance footprint for {@link #hasAttribute(String)} small.
* This shall be accomplished by:
* <ul>
* <li>The method shall only be a lookup for a supplier, no actual
* computation shall be made.
* <li>The result of the supplier must hold the requirements of the result
* of {@link #getAttribute(String)}. Especially it <strong>must not</strong>
* return {@code null}.
* <li>If the attribute isn't set on the widget, the method must return
* {@code null}.
* </ul>
*
*
* @param attr
* the name of the attribute to look for.
* @return a supplier which will return the actual attribute value or
* {@code null} if the attribute isn't set for the widget.
*/
protected Supplier<String> internalGetAttribute(String attr) {
Widget widget = getWidget();
switch(attr){
case "style":
return () -> swtStyles != null ? swtStyles : "";
case "class":
return () -> Objects.toString(getCSSClass(widget), "");
case "swt-data-class":
return () -> {
Object data = widget.getData();
if (data == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Class<?> clazz = data.getClass(); clazz != Object.class; sb.append(' ')) {
sb.append(clazz.getName());
clazz = clazz.getSuperclass();
}
return sb.toString();
};
default:
Object o = widget.getData(attr.toLowerCase());
if (o != null) {
return o::toString;
}
}
return null;
}
@Override
public String getLocalName() {
return localName;
}
@Override
public String getNamespaceURI() {
return namespaceURI;
}
@Override
public Node getParentNode() {
return null;
}
@Override
public NodeList getChildNodes() {
return this;
}
@Override
public int getLength() {
return 0;
}
@Override
public Node item(int index) {
return null;
}
protected Widget getWidget() {
return (Widget) getNativeWidget();
}
@Override
public String getCSSId() {
Widget widget = getWidget();
Object id = getID(widget);
if (id != null) {
return id.toString();
}
return null;
}
@Override
public String getCSSClass() {
Widget widget = getWidget();
Object id = getCSSClass(widget);
if (id != null) {
return id.toString();
}
return null;
}
@Override
public String getCSSStyle() {
Widget widget = getWidget();
// TODO should have key in CSSSWT
Object id = widget.getData("style");
if (id != null) {
return id.toString();
}
return null;
}
/**
* Called by the CSS engine upon a CSS theme switch. Implementations
* should restore the default value so that the new theme can be applied to
* the application without restart
*/
public void reset() {
}
@Override
public String toString() {
return getClass().getSimpleName() + ": " + getWidget();
}
}