/******************************************************************************* * DialogueEditor * Copyright (C) 2013-2014 Pawel Pastuszak * <p> * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p> * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p> * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package pl.kotcrab.jdialogue.editor.components; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.Rectangle; import pl.kotcrab.jdialogue.editor.Assets; import pl.kotcrab.jdialogue.editor.KotcrabText; import java.util.ArrayList; public abstract class DComponent { private KotcrabText title; /** * Id of this component, 0 for start, other in order 1,2,3,4... Set only when exporting, after exporting it's may be incorrect */ private int id; protected int x, y; protected int ry; // bottom, left point of background protected int height, width; protected Rectangle bounds = new Rectangle(); protected Connector[] inputs; protected Connector[] outputs; protected KotcrabText[] inputsLabels; protected KotcrabText[] outputsLabels; protected ComponentTableModel tableModel; protected boolean visible; public DComponent (String title, int x, int y, int inputs, int outputs) { this.x = x; this.y = y; this.title = new KotcrabText(Assets.consolasFont, title, false, 0, 0); this.title.setScale(0.7f); calcSize(inputs, outputs); this.inputs = new Connector[inputs]; this.outputs = new Connector[outputs]; for (int i = 0; i < this.inputs.length; i++) this.inputs[i] = new Connector(this, true); for (int i = 0; i < this.outputs.length; i++) this.outputs[i] = new Connector(this, false); ry = y - height / 2; distributeConnections(); calcTextPos(); inputsLabels = provideInputLabels(); outputsLabels = provideOutputsLabels(); distributeLabels(); } /** * Finishes desarialization of this object */ public void setup () { calcSize(inputs.length, outputs.length); distributeConnections(); calcTextPos(); distributeLabels(); } private void distributeConnections () { float avY = height - 30; // avaiable space in y coordinate // i have no idea what i'm doing, no seriously why is this working? float avYIn = (avY - (12 * (inputs.length))) / (inputs.length); for (int i = 0; i < inputs.length; i++) inputs[i].setPosition(x, ry + avYIn / 2 + ((avYIn + 12) * (inputs.length - 1 - i))); float avYOut = (avY - (12 * (outputs.length))) / (outputs.length); for (int i = 0; i < outputs.length; i++) outputs[i].setPosition(x + width - 12, ry + avYOut / 2 + ((avYOut + 12) * (outputs.length - 1 - i))); // notes: // 12 is connection height // outputs.length - 1 - i to reverse render order, important when we resize component to disconnect connection from bottom, not from top // but still i don't know why is this working... } protected void calcSize (int inputs, int outputs) { width = (int) (title.getTextBounds().width + 30); if (inputs > outputs) height = (inputs + 1) * 20 + 20; else height = (outputs + 1) * 20 + 20; } public void render (SpriteBatch batch) { title.draw(batch); for (int i = 0; i < inputsLabels.length; i++) if (inputsLabels[i] != null) inputsLabels[i].draw(batch); for (int i = 0; i < outputsLabels.length; i++) if (outputsLabels[i] != null) outputsLabels[i].draw(batch); } public void renderShapes (ShapeRenderer shapeRenderer) { shapeRenderer.setColor(Color.GRAY); shapeRenderer.rect(x, ry, width, height); for (int i = 0; i < inputs.length; i++) inputs[i].render(shapeRenderer); for (int i = 0; i < outputs.length; i++) outputs[i].render(shapeRenderer); } public void renderSelectionOutline (ShapeRenderer shapeRenderer, Color color) { shapeRenderer.setColor(color); shapeRenderer.rect(x, ry, width, height); // outline shapeRenderer.line(x, ry + height - 30, x + width, ry + height - 30); // line under text } public void renderDebug (ShapeRenderer shapeRenderer) { // shapeRenderer.rect(boundingRectangle.x, boundingRectangle.y, boundingRectangle.width, boundingRectangle.height); } /** * Detaches this component from others */ public void detachAll () { for (int i = 0; i < inputs.length; i++) inputs[i].detach(); for (int i = 0; i < outputs.length; i++) outputs[i].detach(); } public void detachAllNotOnList (ArrayList<DComponent> componentList) { for (int i = 0; i < inputs.length; i++) inputs[i].detachNotOnList(componentList); for (int i = 0; i < outputs.length; i++) outputs[i].detachNotOnList(componentList); } /** * Resizes the component, if new number is smaller than current, connection will be detached * @param nInputs New number of inputs * @param nOutputs New number of outputs */ public void resize (int nInputs, int nOutputs) { if (nInputs != inputs.length) { if (nInputs < inputs.length) // is new number is smaller than previous { for (int i = nInputs; i < inputs.length; i++) inputs[i].detach(); Connector[] newInputs = new Connector[nInputs]; System.arraycopy(inputs, 0, newInputs, 0, nInputs); inputs = newInputs; } if (nInputs > inputs.length) { Connector[] newInputs = new Connector[nInputs]; System.arraycopy(inputs, 0, newInputs, 0, inputs.length); for (int i = inputs.length; i < newInputs.length; i++) newInputs[i] = new Connector(this, true); inputs = newInputs; } } if (nOutputs != outputs.length) { if (nOutputs < outputs.length) { for (int i = nOutputs; i < outputs.length; i++) outputs[i].detach(); Connector[] newOutputs = new Connector[nOutputs]; System.arraycopy(outputs, 0, newOutputs, 0, nOutputs); outputs = newOutputs; } if (nOutputs > outputs.length) { Connector[] newOutputs = new Connector[nOutputs]; System.arraycopy(outputs, 0, newOutputs, 0, outputs.length); for (int i = outputs.length; i < newOutputs.length; i++) newOutputs[i] = new Connector(this, false); outputs = newOutputs; } } calcSize(inputs.length, outputs.length); ry = y - height / 2; calcTextPos(); distributeConnections(); distributeLabels(); } public boolean contains (float x, float y) { return this.x <= x && this.x + this.width >= x && this.y - this.height / 2 <= y && this.y + this.height / 2 >= y; } public boolean contains (Rectangle rectangle) { float xmin = rectangle.x; float xmax = xmin + rectangle.width; float ymin = rectangle.y; float ymax = ymin + rectangle.height; return ((xmin > x && xmin < x + width) && (xmax > x && xmax < x + width)) && ((ymin > y && ymin < y + height / 2) && (ymax > y && ymax < y + height / 2)); } public Connector connectionContains (float x, float y) { for (int i = 0; i < inputs.length; i++) { if (inputs[i].contains(x, y)) return inputs[i]; } for (int i = 0; i < outputs.length; i++) { if (outputs[i].contains(x, y)) return outputs[i]; } return null; } private void calcTextPos () { this.title.center(width); this.title.setPosition(x + this.title.getPosition().x, ry + height - 30); } protected void distributeLabels () { for (int i = 0; i < inputsLabels.length; i++) { if (inputsLabels[i] != null) { inputsLabels[i].setPosition(inputs[i].getX() + 16, inputs[i].getY() - 14); inputsLabels[i].setScale(0.5f); } } for (int i = 0; i < outputsLabels.length; i++) { if (outputsLabels[i] != null) { outputsLabels[i].setPosition(outputs[i].getX() - outputsLabels[i].getTextBounds().width / 2 - 6, outputs[i].getY() - 14); outputsLabels[i].setScale(0.5f); } } } public void setX (int x) { this.x = x; } public void setY (int y) { this.y = y; ry = y - height / 2; calcTextPos(); distributeConnections(); distributeLabels(); } public KotcrabText[] provideInputLabels () { return new KotcrabText[0]; } public KotcrabText[] provideOutputsLabels () { return new KotcrabText[0]; } public int getX () { return x; } public int getY () { return y; } public int getHeight () { return height; } public int getWidth () { return width; } public Connector[] getOutputs () { return outputs; } public Connector[] getInputs () { return inputs; } public void setInputs (Connector[] inputs) { this.inputs = inputs; } public void setOutputs (Connector[] outputs) { this.outputs = outputs; } public void setId (int id) { this.id = id; } public int getId () { return id; } public ComponentTableModel getTableModel () { return tableModel; } public void setTableModelData (Object[][] data) { tableModel.data = data; } protected void setListeners () { } public void calcIfVisible (Rectangle cameraRect) { if (cameraRect.overlaps(bounds.set(x, y - height / 2, width, height))) visible = true; else visible = false; } public boolean isVisible () { return visible; } }