/extensions/org/mt4jx/components/visibleComponents/widgets/menus/MTSquareMenu.java
http://mt4j.googlecode.com/ · Java · 627 lines · 383 code · 112 blank · 132 comment · 64 complexity · 355c340bb8e705b7f6b8ce289429bf00 MD5 · raw file
- package org.mt4jx.components.visibleComponents.widgets.menus;
-
-
- import java.util.ArrayList;
- import java.util.List;
-
- import org.mt4j.MTApplication;
- import org.mt4j.components.MTComponent;
- import org.mt4j.components.TransformSpace;
- import org.mt4j.components.clipping.Clip;
- import org.mt4j.components.css.style.CSSFont;
- import org.mt4j.components.css.style.CSSStyle;
- import org.mt4j.components.css.util.CSSFontManager;
- import org.mt4j.components.css.util.CSSKeywords.CSSFontWeight;
- import org.mt4j.components.css.util.CSSStylableComponent;
- import org.mt4j.components.visibleComponents.font.IFont;
- import org.mt4j.components.visibleComponents.shapes.MTPolygon;
- import org.mt4j.components.visibleComponents.shapes.MTRectangle;
- import org.mt4j.components.visibleComponents.widgets.MTTextArea;
- import org.mt4j.input.inputProcessors.IGestureEventListener;
- import org.mt4j.input.inputProcessors.MTGestureEvent;
- import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapEvent;
- import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor;
- import org.mt4j.util.MTColor;
- import org.mt4j.util.math.Vector3D;
- import org.mt4j.util.math.Vertex;
-
- import processing.core.PConstants;
- import processing.core.PImage;
-
- /**
- * The Class MTSquareMenu.
- * Menu that contains Squares as buttons containing text or images
- */
- public class MTSquareMenu extends MTRectangle implements CSSStylableComponent {
-
- /** The app. */
- private MTApplication app;
-
- /** The menu contents. */
- private List<MTRectangle> menuContents = new ArrayList<MTRectangle>();
-
- /** The layout. */
- private List<ArrayList<MTRectangle>> layout = new ArrayList<ArrayList<MTRectangle>>();
-
- /** The size. */
- private float size;
-
- /** The current item. */
- private int current = 0;
-
- /** The max per line. */
- private int maxPerLine = 0;
-
- /** The bezel. */
- private float bezel = 10f;
-
- // List of the Child Polygons and their IGestureEventListeners
- /** The polygon listeners. */
- private List<PolygonListeners> polygonListeners = new ArrayList<PolygonListeners>();
-
- /** The menu items. */
- private List<MenuItem> menuItems = new ArrayList<MenuItem>();
-
- /**
- * Instantiates a new MTSquareMenu
- *
- * @param app the app
- * @param position the position of the Menu
- * @param menuItems the menu items
- * @param size the size of the squares
- */
- public MTSquareMenu(MTApplication app, Vector3D position,
- List<MenuItem> menuItems, float size) {
- super(app, position.x, position.y, (float) (int) Math
- .sqrt(menuItems.size() + 1) * size, (float) (int) Math
- .sqrt(menuItems.size() + 1) * size);
- this.app = app;
- this.size = size;
-
- // Set the Rectangle to be invisible
- this.setCssForceDisable(true);
- this.setNoFill(true);
- this.setNoStroke(true);
-
- this.menuItems = menuItems;
- this.createMenuItems();
-
- //Register the TapProcessor
- this.setGestureAllowance(TapProcessor.class, true);
- this.registerInputProcessor(new TapProcessor(app));
- this.addGestureListener(TapProcessor.class, new TapListener(polygonListeners));
- this.setCssForceDisable(true);
-
-
-
- }
-
- public void createMenuItems() {
- for (MTComponent c : this.getChildren()) {
- c.destroy();
- }
- menuContents.clear();
- polygonListeners.clear();
-
- for (MenuItem s : menuItems) {
-
- if (s != null && s.getType() == MenuItem.TEXT) {
- //Create a new menu cell
- MTRectangle container = new MTRectangle(app, 0, 0, size, size);
- this.addChild(container);
-
- // Add MTTextArea Children to take single lines of the Menu Text
- for (String t : s.getMenuText().split("\n")) {
- MTTextArea menuItem = new MTTextArea(app);
- menuItem.setText(t);
- menuItem.setCssForceDisable(true);
- menuItem.setFillColor(new MTColor(0, 0, 0, 0));
- menuItem.setStrokeColor(new MTColor(0, 0, 0, 0));
- menuItem.setPickable(false);
- container.addChild(menuItem);
-
- }
-
- container.setChildClip(new Clip(container));
- container.setPickable(false);
- polygonListeners.add(new PolygonListeners(container, s.getGestureListener()));
- menuContents.add(container);
- } else if (s != null && s.getType() == MenuItem.PICTURE) {
-
- if (s.getMenuImage() != null) {
- PImage texture = null;
- // If Image doesn't fit, make it fit!
- if (s.getMenuImage().width != (int) size
- || s.getMenuImage().height != (int) size) {
- texture = cropImage(s.getMenuImage(), (int) size, true);
- } else {
- texture = s.getMenuImage();
- }
-
-
- //Create a new menu cell
- MTRectangle container = new MTRectangle(app, 0, 0, size,
- size);
- this.addChild(container);
- //Set the background texture
- container.setTexture(texture);
-
- container.setChildClip(new Clip(container));
- container.setPickable(false);
- polygonListeners.add(new PolygonListeners(container, s.getGestureListener()));
- menuContents.add(container);
- }
-
- }
-
- }
-
- //Apply Style to Children
- this.styleChildren(getNecessaryFontSize(menuItems, size));
-
- }
-
-
- /**
- * Gets the size.
- *
- * @return the size
- */
- public float getSize() {
- return size;
- }
-
-
- /**
- * Sets the menu items and reinstantiates the menu
- *
- * @param menuItems the new menu items
- */
- public void setMenuItems(List<MenuItem> menuItems) {
- this.menuItems = menuItems;
- createMenuItems();
- }
-
-
-
- /**
- * Sets the size.
- *
- * @param size the new size
- */
- public void setSize(float size) {
- this.size = size;
- this.createMenuItems();
- }
-
- /**
- * Calculates the total height of a number of MTTextAreas
- *
- * @param components the components
- * @return the height
- */
- private float calcTotalHeight(MTComponent[] components) {
- float height = 0;
- for (MTComponent c : components) {
- if (c instanceof MTTextArea)
- height += ((MTTextArea) c).getHeightXY(TransformSpace.LOCAL);
- }
-
- return height;
- }
-
- /**
- * Crop image.
- *
- * @param image the image
- * @param size the size
- * @param resize Force-resize the image?
- * @return the cropped image
- */
- private PImage cropImage(PImage image, int size, boolean resize) {
- PImage workingCopy;
- try {
- workingCopy= (PImage) image.clone();
- } catch (CloneNotSupportedException e) {
- System.out.println("Cloning not supported!");
- workingCopy = image;
- }
-
-
-
- //Crops (and resizes) an Image to fit into the suqare
- PImage returnImage = app.createImage(size, size, PConstants.RGB);
-
- //Resize Image
- if (resize || workingCopy.width < size || workingCopy.height < size) {
- if (workingCopy.width < workingCopy.height) {
- workingCopy.resize(
- size,
- (int) ((float) workingCopy.height / ((float) workingCopy.width / (float) size)));
- } else {
- workingCopy.resize(
- (int) ((float) workingCopy.width / ((float) workingCopy.height / (float) size)),
- size);
- }
-
- }
-
- // Crop Starting Points
- int x = (workingCopy.width / 2) - (size / 2);
- int y = (workingCopy.height / 2) - (size / 2);
-
- // Bugfixing: Don't Allow Out-of-Bounds coordinates
- if (x + size > workingCopy.width)
- x = workingCopy.width - size;
- if (x < 0)
- x = 0;
- if (x + size > workingCopy.width)
- size = workingCopy.width - x;
- if (y + size > workingCopy.height)
- x = workingCopy.height - size;
- if (y < 0)
- y = 0;
- if (y + size > workingCopy.height)
- size = workingCopy.height - y;
-
- //Crop Image
- returnImage.copy(workingCopy, x, y, size, size, 0, 0, size, size);
-
- return returnImage;
- }
-
- /**
- * Gets the maximum font size for a certain width
- *
- * @param strings the strings
- * @param size the width
- * @return the maximum font size
- */
- private int getNecessaryFontSize(List<MenuItem> strings, float size) {
- int maxNumberCharacters = 0;
-
- for (MenuItem s : strings) {
-
- if (s.getType() == MenuItem.TEXT) {
- if (s.getMenuText().contains("\n")) {
- for (String t : s.getMenuText().split("\n")) {
-
- if (t.length() > maxNumberCharacters)
- maxNumberCharacters = t.length();
-
- }
- } else {
-
- if (s.getMenuText().length() > maxNumberCharacters)
- maxNumberCharacters = s.getMenuText().length();
-
- }
- }
- }
-
- float spc = size / (float) maxNumberCharacters; // Space Per Character
- int returnValue = (int)(-0.5 + 1.725 * spc); //Determined using Linear Regression
- return returnValue;
- }
-
- /**
- * Returns the next n items
- *
- * @param next the number of items to return
- * @return the list of n next items
- */
- private List<MTRectangle> next(int next) {
- List<MTRectangle> returnValues = new ArrayList<MTRectangle>();
- for (int i = 0; i < next; i++) {
- returnValues.add(menuContents.get(current++));
- }
- return returnValues;
- }
-
- /**
- * Distribute the menu cells on the rows.
- */
- private void organizeRectangles() {
- layout.clear();
- layout.add(new ArrayList<MTRectangle>());
- layout.add(new ArrayList<MTRectangle>());
- layout.add(new ArrayList<MTRectangle>());
- layout.add(new ArrayList<MTRectangle>());
- current = 0;
- switch (menuContents.size()) {
- case 0:
- case -1:
- break;
-
- case 1:
- layout.get(0).addAll(next(1));
- maxPerLine = 1;
- break;
- case 2:
- layout.get(0).addAll(next(2));
- maxPerLine = 2;
- break;
- case 3:
- layout.get(0).addAll(next(1));
- layout.get(1).addAll(next(2));
- maxPerLine = 2;
- break;
- case 4:
- layout.get(0).addAll(next(2));
- layout.get(1).addAll(next(2));
- maxPerLine = 2;
- break;
- case 5:
- layout.get(0).addAll(next(2));
- layout.get(1).addAll(next(3));
- maxPerLine = 3;
- break;
- case 6:
- layout.get(0).addAll(next(3));
- layout.get(1).addAll(next(3));
- maxPerLine = 3;
- break;
- case 7:
- layout.get(0).addAll(next(2));
- layout.get(1).addAll(next(3));
- layout.get(2).addAll(next(2));
- maxPerLine = 3;
- break;
- case 8:
- layout.get(0).addAll(next(3));
- layout.get(1).addAll(next(2));
- layout.get(2).addAll(next(3));
- maxPerLine = 3;
- break;
- case 9:
- layout.get(0).addAll(next(3));
- layout.get(1).addAll(next(3));
- layout.get(2).addAll(next(3));
- maxPerLine = 3;
- break;
- case 10:
- layout.get(0).addAll(next(3));
- layout.get(1).addAll(next(4));
- layout.get(2).addAll(next(3));
- maxPerLine = 4;
- break;
- case 11:
- layout.get(0).addAll(next(4));
- layout.get(1).addAll(next(3));
- layout.get(2).addAll(next(4));
- maxPerLine = 4;
- break;
- case 12:
- layout.get(0).addAll(next(4));
- layout.get(1).addAll(next(4));
- layout.get(2).addAll(next(4));
- maxPerLine = 4;
- break;
- case 13:
- layout.get(0).addAll(next(4));
- layout.get(1).addAll(next(5));
- layout.get(2).addAll(next(4));
- maxPerLine = 5;
- break;
- case 14:
- layout.get(0).addAll(next(3));
- layout.get(1).addAll(next(4));
- layout.get(2).addAll(next(4));
- layout.get(3).addAll(next(3));
- maxPerLine = 4;
- break;
- case 15:
- layout.get(0).addAll(next(5));
- layout.get(1).addAll(next(5));
- layout.get(2).addAll(next(5));
- maxPerLine = 5;
- break;
- case 16:
- layout.get(0).addAll(next(4));
- layout.get(1).addAll(next(4));
- layout.get(2).addAll(next(4));
- layout.get(3).addAll(next(4));
- maxPerLine = 4;
- break;
- default:{
- System.err.println("Unsupported number of menu items in: " + this);
- }
- }
-
- }
-
-
-
-
- /**
- * Style the cells of the menu.
- *
- * @param fontsize the font-size
- */
- private void styleChildren(int fontsize) {
- organizeRectangles();
- CSSStyle vss = this.getCssHelper().getVirtualStyleSheet();
- CSSFont cf = this.getCssHelper().getVirtualStyleSheet().getCssfont().clone();
- // Style Font: Bold + fitting fontsize
- cf.setFontsize(fontsize);
- cf.setWeight(CSSFontWeight.BOLD);
-
- //Load Font
- CSSFontManager cfm = new CSSFontManager(app);
- IFont font = cfm.selectFont(cf);
-
- for (MTRectangle c : menuContents) {
-
- MTRectangle rect = c;
-
- c.setWidthLocal(size);
- c.setHeightLocal(size);
-
-
- //Set Stroke/Border
- rect.setStrokeColor(vss.getBorderColor());
- rect.setStrokeWeight(vss.getBorderWidth());
-
- // Set Font and Position for the child MTTextAreas
- if (((MTRectangle) c).getTexture() == null) {
- rect.setFillColor(vss.getBackgroundColor());
- for (MTComponent d : c.getChildren()) {
- if (d instanceof MTTextArea) {
- MTTextArea ta = (MTTextArea) d;
- ta.setFont(font);
- }
- }
-
- float height = calcTotalHeight(c.getChildren());
- float ypos = size / 2f - height / 2f;
- for (MTComponent d : c.getChildren()) {
- if (d instanceof MTTextArea) {
- MTTextArea ta = (MTTextArea) d;
-
- ta.setPositionRelativeToParent(new Vector3D(size / 2f,
- ypos + ta.getHeightXY(TransformSpace.LOCAL)
- / 2f));
- ypos += ta.getHeightXY(TransformSpace.LOCAL);
-
- }
-
- }
- } else {
- //Set FillColor for the image (neutral white)
- rect.setFillColor(MTColor.WHITE);
- }
-
- }
-
- //Min/Max Values of the Children
- float minx = 16000, maxx = -16000, miny = 16000, maxy = -16000;
- int currentRow = 0;
-
- // Position the Polygons in the grid
- for (List<MTRectangle> lr : layout) {
- int currentColumn = 0;
- for (MTRectangle r : lr) {
- r.setPositionRelativeToParent((new Vector3D(this
- .getVerticesLocal()[0].x
- + (size / 2f)
- + (bezel / 2f)
- + currentColumn++
- * (size + bezel)
- + (maxPerLine - lr.size()) * (size / 2f + bezel / 2f),
- this.getVerticesLocal()[0].x + (size / 2 + bezel / 2f)
- + currentRow * (size + bezel))));
- //Determine Min/Max-Positions
- //We have to use the childrens relative-to parent vertices for the calculation of this component's local vertices
- Vertex[] unTransformedCopy = Vertex.getDeepVertexArrayCopy(r.getGeometryInfo().getVertices());
- //transform the copied vertices and save them in the vertices array
- Vertex[] verticesRelParent = Vertex.transFormArray(r.getLocalMatrix(), unTransformedCopy);
- for (Vertex v: verticesRelParent) {
- if (v.x < minx) minx = v.x;
- if (v.x > maxx) maxx = v.x;
- if (v.y < miny) miny = v.y;
- if (v.y > maxy) maxy = v.y;
- }
- }
- currentRow++;
- }
-
- MTColor fill = this.getFillColor();
- //Set Vertices to include all children
- this.setVertices(new Vertex[] {new Vertex(minx,miny, 0, fill.getR(), fill.getG(), fill.getB(), fill.getAlpha()), new Vertex(maxx,miny, 0, fill.getR(), fill.getG(), fill.getB(), fill.getAlpha()), new Vertex(maxx,maxy, 0, fill.getR(), fill.getG(), fill.getB(), fill.getAlpha()), new Vertex(minx,maxy, 0, fill.getR(), fill.getG(), fill.getB(), fill.getAlpha()),new Vertex(minx,miny, 0, fill.getR(), fill.getG(), fill.getB(), fill.getAlpha())});
- }
-
-
-
-
- /**
- * The listener interface for receiving tap events.
- * The class that is interested in processing a tap
- * event implements this interface, and the object created
- * with that class is registered with a component using the
- * component's <code>addTapListener<code> method. When
- * the tap event occurs, that object's appropriate
- * method is invoked.
- *
- * @see TapEvent
- */
- public class TapListener implements IGestureEventListener {
- //Tap Listener to reach through TapListeners to children
-
- /** The children. */
- List<PolygonListeners> children;
-
- /**
- * Instantiates a new tap listener.
- *
- * @param children the children
- */
- public TapListener(List<PolygonListeners> children) {
- this.children = children;
- }
-
-
- /* (non-Javadoc)
- * @see org.mt4j.input.inputProcessors.IGestureEventListener#processGestureEvent(org.mt4j.input.inputProcessors.MTGestureEvent)
- */
- public boolean processGestureEvent(MTGestureEvent ge) {
- if (ge instanceof TapEvent) {
-
- TapEvent te = (TapEvent)ge;
- if (te.getTapID() == TapEvent.TAPPED) {
- //Vector3D w = Tools3D.project(app, app.getCurrentScene().getSceneCam(), te.getLocationOnScreen());
- for (PolygonListeners pl: children) {
- pl.component.setPickable(true);
- if (
- // pl.component.getIntersectionGlobal(Tools3D
- // .getCameraPickRay(app, pl.component, te.getCursor().getPosition().x,
- // te.getCursor().getPosition().y)) != null
- pl.component.getIntersectionGlobal(te.getCursor()) != null
-
- ) {
- pl.listener.processGestureEvent(ge);
- } else {
-
- }
- pl.component.setPickable(false);
- }
- }
- }
- return false;
- }
-
-
-
-
- }
-
-
-
-
- /**
- * The Class PolygonListeners.
- */
- public class PolygonListeners {
-
- /** The component. */
- public MTPolygon component;
-
- /** The listener. */
- public IGestureEventListener listener;
-
- /**
- * Instantiates a new polygon listeners.
- *
- * @param component the component
- * @param listener the listener
- */
- public PolygonListeners(MTPolygon component, IGestureEventListener listener) {
- this.component = component;
- this.listener = listener;
- }
-
- }
-
-
-
- }