package org.mt4j.components.css.util; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.mt4j.AbstractMTApplication; import org.mt4j.components.MTComponent; import org.mt4j.components.StateChange; import org.mt4j.components.StateChangeEvent; import org.mt4j.components.StateChangeListener; import org.mt4j.components.TransformSpace; import org.mt4j.components.clipping.Clip; import org.mt4j.components.css.style.CSSStyle; import org.mt4j.components.css.style.CSSStyle.BackgroundRepeat; import org.mt4j.components.css.style.CSSStyleHierarchy; import org.mt4j.components.css.util.CSSKeywords.Position; import org.mt4j.components.visibleComponents.shapes.MTCSSStylableShape; import org.mt4j.components.visibleComponents.shapes.MTPolygon; import org.mt4j.components.visibleComponents.shapes.MTRectangle; import org.mt4j.util.MT4jSettings; import org.mt4j.util.MTColor; import org.mt4j.util.math.Tools3D; import org.mt4j.util.math.Vector3D; import org.mt4j.util.math.Vertex; import org.mt4j.util.opengl.GLTexture; import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER; import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER; import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET; import org.mt4j.util.opengl.GLTexture.WRAP_MODE; import org.mt4j.util.opengl.GLTextureSettings; import processing.core.PImage; /** * The Class CSSHelper. */ public class CSSHelper { /** The private style sheets (unique to an object) */ private List<CSSStyle> privateStyleSheets = new ArrayList<CSSStyle>(); /** The currently relevant global style sheet */ private List<CSSStyleHierarchy> sheets = new ArrayList<CSSStyleHierarchy>(); /** The virtual style sheet (generated from the global and private style sheets) */ private CSSStyle virtualStyleSheet = null; /** The CSS style manager. */ private CSSStyleManager cssStyleManager; /** The MTApplication. */ private AbstractMTApplication app; /** The MTComponent. */ private MTComponent c; /** * Instantiates a new CSS helper. * * @param c the MTComponent * @param a the MTApplication */ public CSSHelper(MTComponent c, AbstractMTApplication a) { this.c = c; this.app = a; this.cssStyleManager = a.getCssStyleManager(); addListeners(); } /** * Instantiates a new CSS helper. * * @param c the MTComponent * @param a the MTApplication * @param s the new private CSSStyle */ public CSSHelper(MTComponent c, AbstractMTApplication a, CSSStyle s) { this(c,a); this.getPrivateStyleSheets().add(s); } /** * Instantiates a new CSS helper. * * @param c the MTComponent * @param a the MTApplication * @param s the list of private style sheets */ public CSSHelper(MTComponent c, AbstractMTApplication a, List<CSSStyle> s) { this(c,a); this.getPrivateStyleSheets().addAll(s); } /** * Adds the listeners to the MTComponent, so applyStyleSheet() is called every time the component is added as child */ private void addListeners() { if (c instanceof MTCSSStylableShape) { final MTCSSStylableShape cssShape = (MTCSSStylableShape) c; cssShape.addStateChangeListener(StateChange.ADDED_TO_PARENT, new StateChangeListener() { public void stateChanged(StateChangeEvent evt) { cssShape.applyStyleSheet();//rather leave the applyStyleSheet() method and implementation to the component itself // applyStyleSheet(CSSHelper.this.c); } }); } } /** * Apply the style sheet. Disambiguate between different subclasses of MTComponent */ public void applyStyleSheet(MTComponent c) { //This method can be used by a component which only implements the cssstylable interface and //doesent extend MTCssStylableShape to get some standard behaviour //like the calling of applyStyleSheet on all their children if (c instanceof CSSStylableComponent) { CSSStylableComponent sc = (CSSStylableComponent)c; if (!sc.isCssForceDisabled() && ((sc.isCSSStyled() && !app.getCssStyleManager().isGloballyDisabled()) || app.getCssStyleManager().isGloballyEnabled())) { evaluateStyleSheets(); for (MTComponent d : c.getChildren()) { if (d instanceof CSSStylableComponent) { CSSStylableComponent s = (CSSStylableComponent) d; s.applyStyleSheet(); } } } } } /** * Evaluate the style sheets (in order of relevance). */ private void evaluateStyleSheets() { sheets = cssStyleManager.getRelevantStyles(c); Collections.sort(sheets); virtualStyleSheet = new CSSStyle(app); for (CSSStyleHierarchy h : sheets) { virtualStyleSheet.addStyleSheet(h.getStyle()); } for (CSSStyle s : privateStyleSheets) { virtualStyleSheet.addStyleSheet(s); } } /** * Gets the private style sheets. * * @return the private style sheets */ public List<CSSStyle> getPrivateStyleSheets() { return privateStyleSheets; } /** * Gets the currently relevant style sheets. * * @return the sheets */ public List<CSSStyleHierarchy> getSheets() { return sheets; } /** * Gets the x distance (between a float and a Vertex) * * @param x the x-position * @param v2 the vertex to compare to * @return the x-distance */ private static float getXDistance(float x, Vertex v2) { float distance = v2.x - x; if (distance >= 0) return distance; else return -distance; } /** * Gets the y distance (between a float and a vertex) * * @param y the y-position * @param v2 the vertex to compare to * @return the y-distance */ private static float getYDistance(float y, Vertex v2) { float distance = v2.y - y; if (distance >= 0) return distance; else return -distance; } /** * Sets the private style sheets. * * @param privateStyleSheets the new private style sheets */ public void setPrivateStyleSheets(List<CSSStyle> privateStyleSheets) { this.privateStyleSheets = privateStyleSheets; } /** * Sets the relevant style sheets. * * @param sheets the new sheets */ public void setSheets(List<CSSStyleHierarchy> sheets) { this.sheets = sheets; } /** * Adds a style sheet. * * @param sheet the new style sheet */ public void setStyleSheet(CSSStyle sheet) { this.privateStyleSheets.add(sheet); } /** * Sets the texture for a tiled background. * * @param p the MTPolygon to apply it to * @param bgImage the background-image */ public void setBackground(MTPolygon p) { PImage bgImage = virtualStyleSheet.getBackgroundImage(); if (bgImage != null) { if (virtualStyleSheet.getBackgroundRepeat() != BackgroundRepeat.NONE) { boolean pot = Tools3D.isPowerOfTwoDimension(bgImage); boolean tiled = true; p.setFillColor(MTColor.WHITE); if (tiled) { // Generate texture coordinates to repeat the texture over the whole // background (works only with OpenGL) Vertex[] backgroundVertices = p.getVerticesLocal(); float minx, miny; if (backgroundVertices.length > 0) { minx = backgroundVertices[0].x; miny = backgroundVertices[0].y; for (Vertex vtx : backgroundVertices) { if (vtx.x < minx) minx = vtx.x; if (vtx.y < miny) miny = vtx.y; } for (Vertex vtx : backgroundVertices) { vtx.setTexCoordU(getXDistance(minx, vtx) / bgImage.width); vtx.setTexCoordV(getYDistance(miny, vtx) / bgImage.height); } } // Update changed texture coordinates for opengl buffer drawing if (MT4jSettings.getInstance().isOpenGlMode()) p.getGeometryInfo().updateTextureBuffer(p.isUseVBOs()); } WRAP_MODE horizontal = WRAP_MODE.CLAMP, vertical = WRAP_MODE.CLAMP; switch (virtualStyleSheet.getBackgroundRepeat()) { case REPEAT: horizontal = WRAP_MODE.REPEAT; vertical = WRAP_MODE.REPEAT; case XREPEAT: horizontal = WRAP_MODE.REPEAT; case YREPEAT: vertical = WRAP_MODE.REPEAT; } if (MT4jSettings.getInstance().isOpenGlMode()) { GLTextureSettings g = new GLTextureSettings( TEXTURE_TARGET.TEXTURE_2D, SHRINKAGE_FILTER.BilinearNoMipMaps, EXPANSION_FILTER.Bilinear, horizontal, vertical); GLTexture tex; if (pot) { tex = new GLTexture(app, bgImage, g); } else { if (tiled) { g.target = TEXTURE_TARGET.RECTANGULAR; g.shrinkFilter = SHRINKAGE_FILTER.Trilinear; // Because NPOT texture with GL_REPEAT isnt supported // -> gluBuild2Dmipmapds strechtes the texture to POT size tex = new GLTexture(app, bgImage, g); } else { g.target = TEXTURE_TARGET.RECTANGULAR; tex = new GLTexture(app, bgImage, g); } } p.setTexture(tex); } else { p.setTexture(bgImage); } } else { if (virtualStyleSheet.getBackgroundPosition() != null) { MTRectangle img = new MTRectangle(app,bgImage); p.addChild(img); img.setPickable(false); float xPos = 0; float yPos = 0; switch (virtualStyleSheet.getBackgroundPosition().getxType()) { case ABSOLUTE: xPos = virtualStyleSheet.getBackgroundPosition().getxPos(); break; case RELATIVE: xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getxPos(), true); break; case KEYWORD: xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getxKeywordPosition(), true); break; } switch (virtualStyleSheet.getBackgroundPosition().getyType()) { case ABSOLUTE: xPos = virtualStyleSheet.getBackgroundPosition().getyPos(); break; case RELATIVE: xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getyPos(), false); break; case KEYWORD: xPos = determineAbsolutePosition(p, virtualStyleSheet.getBackgroundPosition().getyKeywordPosition(), false); break; } img.setPositionRelativeToParent( p.getVerticesLocal()[0].addLocal(calcPos(p, virtualStyleSheet.getBackgroundImage(), xPos, yPos))); Clip c = new Clip(app, p.getBounds().getVectorsLocal()[0].x,p.getBounds().getVectorsLocal()[0].y,p.getBounds().getWidthXY(TransformSpace.LOCAL),p.getBounds().getHeightXY(TransformSpace.LOCAL)); img.setClip(c); //p.setChildClip(new Clip(p)); } else { p.setTexture(bgImage); } } } } private float determineAbsolutePosition(MTPolygon p, Position po, boolean isHorizontal) { float returnValue = 0; if (isHorizontal) { switch (po) { case LEFT: return calcPos(p,virtualStyleSheet.getBackgroundImage(), 0,0).x; case RIGHT: return calcPos(p,virtualStyleSheet.getBackgroundImage(), p.getWidthXY(TransformSpace.LOCAL) - (float)virtualStyleSheet.getBackgroundImage().width,0).x; case CENTER: return calcPos(p,virtualStyleSheet.getBackgroundImage(), (p.getWidthXY(TransformSpace.LOCAL) / 2f) - ((float)virtualStyleSheet.getBackgroundImage().width/2f),0).x; } } else { switch (po) { case TOP: return calcPos(p,virtualStyleSheet.getBackgroundImage(), 0,0).y; case BOTTOM: return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0, p.getHeightXY(TransformSpace.LOCAL) - (float)virtualStyleSheet.getBackgroundImage().height).y; case CENTER: return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0, (p.getHeightXY(TransformSpace.LOCAL)/2f) - ((float)virtualStyleSheet.getBackgroundImage().height / 2f)).y; } } return returnValue; } private float determineAbsolutePosition(MTPolygon p, float po, boolean isHorizontal) { if (isHorizontal) { return calcPos(p, virtualStyleSheet.getBackgroundImage(), p.getWidthXY(TransformSpace.LOCAL) * po ,0).x; } else { return calcPos(p, virtualStyleSheet.getBackgroundImage(), 0 ,p.getHeightXY(TransformSpace.LOCAL) * po).x; } } private Vector3D calcPos(MTPolygon box, PImage ta, float xo, float yo) { return new Vector3D((ta.width / 2) + xo, (ta.height / 2) + yo); } public CSSStyle getVirtualStyleSheet() { evaluateStyleSheets(); return virtualStyleSheet; } }