/* * Copyright 2014 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.logic.behavior.nui; import com.google.common.collect.Lists; import org.terasology.utilities.Assets; import org.terasology.input.Keyboard; import org.terasology.input.MouseInput; import org.terasology.input.device.KeyboardDevice; import org.terasology.logic.behavior.BehaviorNodeComponent; import org.terasology.logic.behavior.tree.Node; import org.terasology.logic.behavior.tree.Status; import org.terasology.logic.behavior.tree.TreeAccessor; import org.terasology.math.geom.Vector2i; import org.terasology.math.geom.Vector2f; import org.terasology.rendering.assets.texture.TextureRegion; import org.terasology.rendering.nui.BaseInteractionListener; import org.terasology.rendering.nui.Canvas; import org.terasology.rendering.nui.CoreWidget; import org.terasology.rendering.nui.InteractionListener; import org.terasology.rendering.nui.events.NUIMouseClickEvent; import org.terasology.rendering.nui.events.NUIMouseDragEvent; import org.terasology.rendering.nui.events.NUIMouseOverEvent; import org.terasology.rendering.nui.events.NUIMouseReleaseEvent; import org.terasology.rendering.nui.layouts.ZoomableLayout; import java.util.List; /** * A widget to render and process inputs for a node of a behavior tree. * */ public class RenderableNode extends CoreWidget implements ZoomableLayout.PositionalWidget<BehaviorEditor>, TreeAccessor<RenderableNode> { private TextureRegion texture = Assets.getTextureRegion("engine:button").get(); private final List<RenderableNode> children = Lists.newArrayList(); private PortList portList; private Node node; private Vector2f position; private Vector2f size; private TreeAccessor<RenderableNode> withoutModel; private TreeAccessor<RenderableNode> withModel; private BehaviorNodeComponent data; private Vector2i last; private BehaviorEditor editor; private boolean dragged; private Status status; private boolean collapsed; private boolean copyMode; private InteractionListener moveListener = new BaseInteractionListener() { @Override public void onMouseOver(NUIMouseOverEvent event) { } @Override public boolean onMouseClick(NUIMouseClickEvent event) { last = event.getRelativeMousePosition(); MouseInput button = event.getMouseButton(); KeyboardDevice keyboard = event.getKeyboard(); dragged = false; copyMode = button == MouseInput.MOUSE_LEFT && (keyboard.isKeyDown(Keyboard.KeyId.LEFT_SHIFT) || keyboard.isKeyDown(Keyboard.KeyId.RIGHT_SHIFT)); if (copyMode) { editor.copyNode(RenderableNode.this); } return true; } @Override public void onMouseRelease(NUIMouseReleaseEvent event) { if (!dragged) { if (event.getMouseButton() == MouseInput.MOUSE_RIGHT) { collapsed = !collapsed; for (RenderableNode child : children) { child.setVisible(!collapsed); } } else { editor.nodeClicked(RenderableNode.this); } } dragged = false; } @Override public void onMouseDrag(NUIMouseDragEvent event) { Vector2f diff = editor.screenToWorld(event.getRelativeMousePosition()); diff.sub(editor.screenToWorld(last)); if (diff.lengthSquared() != 0) { dragged = true; } move(diff); last = event.getRelativeMousePosition(); } }; public RenderableNode() { this(null); } public RenderableNode(BehaviorNodeComponent data) { this.data = data; position = new Vector2f(); size = new Vector2f(10, 5); portList = new PortList(this); withoutModel = new ChainedTreeAccessor<>(this, portList); withModel = new ChainedTreeAccessor<>(this, portList, new NodeTreeAccessor()); } @Override public void onDraw(Canvas canvas) { canvas.drawTexture(texture); String text = getData().name + " " + (status != null ? status : ""); if (collapsed) { text += "[+]"; } canvas.drawText(text); if (editor != null) { canvas.addInteractionRegion(moveListener, data.description); } portList.onDraw(canvas); } @Override public Vector2i getPreferredContentSize(Canvas canvas, Vector2i sizeHint) { return sizeHint; } public void update() { List<RenderableNode> all = Lists.newArrayList(children); children.clear(); for (RenderableNode renderableNode : all) { withoutModel.insertChild(-1, renderableNode); } } @Override public void onAdded(BehaviorEditor layout) { this.editor = layout; } @Override public void onRemoved(BehaviorEditor layout) { this.editor = null; } public TreeAccessor<RenderableNode> withoutModel() { return withoutModel; } public TreeAccessor<RenderableNode> withModel() { return withModel; } public PortList getPortList() { return portList; } public void setNode(Node node) { this.node = node; } public void setPosition(Vector2f position) { this.position = position; } public void setPosition(float x, float y) { position = new Vector2f(x, y); } public void move(Vector2f diff) { position = new Vector2f(position); position.add(diff); for (RenderableNode child : children) { child.move(diff); } } public BehaviorEditor getEditor() { return editor; } public void setSize(Vector2f size) { this.size = size; } public Node getNode() { return node; } public BehaviorNodeComponent getData() { return data; } public Port.InputPort getInputPort() { return getPortList().getInputPort(); } public Iterable<Port> getPorts() { return getPortList().ports(); } @Override public void insertChild(int index, RenderableNode child) { if (index == -1) { children.add(child); } else { children.add(index, child); } } @Override public void setChild(int index, RenderableNode child) { if (children.size() == index) { children.add(null); } if (children.get(index) != null) { Port.InputPort inputPort = children.get(index).getInputPort(); inputPort.setTarget(null); } children.set(index, child); } @Override public RenderableNode removeChild(int index) { RenderableNode remove = children.remove(index); remove.getInputPort().setTarget(null); return remove; } @Override public RenderableNode getChild(int index) { if (children.size() > index) { return children.get(index); } return null; } @Override public int getChildrenCount() { return children.size(); } public List<RenderableNode> children() { return children; } @Override public int getMaxChildren() { return getNode().getMaxChildren(); } @Override public Vector2f getPosition() { return position; } @Override public Vector2f getSize() { return size; } @Override public String toString() { return getNode() != null ? getNode().toString() : ""; } public void visit(Visitor visitor) { visitor.visit(this); for (RenderableNode child : children) { child.visit(visitor); } } public void setStatus(Status status) { this.status = status; } public Status getStatus() { return status; } @Override public void setVisible(boolean visible) { super.setVisible(visible); for (RenderableNode child : children) { child.setVisible(visible); } } public interface Visitor { void visit(RenderableNode node); } public class NodeTreeAccessor implements TreeAccessor<RenderableNode> { @Override public void insertChild(int index, RenderableNode child) { getNode().insertChild(index, child.getNode()); } @Override public void setChild(int index, RenderableNode child) { getNode().setChild(index, child.getNode()); } @Override public RenderableNode removeChild(int index) { getNode().removeChild(index); return null; } @Override public RenderableNode getChild(int index) { return null; } @Override public int getChildrenCount() { return getNode().getChildrenCount(); } @Override public int getMaxChildren() { return getNode().getMaxChildren(); } } }