package speiger.src.coreengine.rendering.gui.components; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.sets.ObjectOpenHashSet; import speiger.src.collections.objects.sets.ObjectSet; import speiger.src.coreengine.math.MathUtils; import speiger.src.coreengine.math.misc.ColorUtils; import speiger.src.coreengine.math.misc.Facing; import speiger.src.coreengine.math.vector.ints.Vec2i; import speiger.src.coreengine.rendering.gui.GuiComponent; import speiger.src.coreengine.rendering.gui.base.IButtonComponent; import speiger.src.coreengine.rendering.gui.components.tree.ITreeEntry; import speiger.src.coreengine.rendering.gui.helper.UIShapes; import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; import speiger.src.coreengine.rendering.gui.helper.constrains.Constraints; import speiger.src.coreengine.rendering.gui.renderer.UIRenderer; import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; import speiger.src.coreengine.rendering.input.Keyboard; public class TreeComponent extends GuiComponent implements IButtonComponent { public static final int SELECTION_MODE_DISABLE = 0; public static final int SELECTION_MODE_SINGLE = 1; public static final int SELECTION_MODE_MULTI = 2; public static final int SELECTION_MODE_INTERACT = 3; public static final int UPDATE_MODE_DISABLED = 0; public static final int UPDATE_MODE_VISIBLE = 1; public static final int UPDATE_MODE_OPEN = 2; public static final int UPDATE_MODE_ALL = 3; public static final int FLAG_NO_BACKGROUND = 1 << 20; ScrollBarComponent verticalBar = new ScrollBarComponent(ColorUtils.LIGHT_GRAY); ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorUtils.LIGHT_GRAY).setHorizontal(true); int color; int selectedColor; int hoverColor = ColorUtils.LIGHT_GRAY; int hoverIndex = -1; int dragIndex = -1; int movement; Vec2i lastMouse = Vec2i.newMutable(); IButtonComponent customButton; protected ObjectSet openNodes = new ObjectOpenHashSet<>(); protected ObjectSet selectedNodes = new ObjectOpenHashSet<>(); protected int selectionMode = 1; protected int updateMode = 1; T node; float entryHeight; boolean listChange = true; List visibleNodes = new ObjectArrayList(); RenderBuffer buffer; public TreeComponent(int color, float entryHeight) { super(0F, 0F, 0F, 0F); this.entryHeight = entryHeight; this.color = color; this.selectedColor = color; } public TreeComponent(int color, float entryHeight, T entry) { super(0F, 0F, 0F, 0F); this.entryHeight = entryHeight; getNodes(entry, openNodes, false); this.color = color; this.selectedColor = color; node = entry; if(entry != null) { entry.calculateDebth(); } } public TreeComponent(float x, float y, float width, float height, int color, float entryHeight, T entry) { super(x, y, width, height); this.entryHeight = entryHeight; getNodes(entry, openNodes, false); this.color = color; node = entry; if(entry != null) { entry.calculateDebth(); } } @Override public void init() { addCloseListener(buffer = getRenderer().createBuffer()); addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); List entries = new ObjectArrayList(); getNodes(node, entries, false); for(int i = 0, m = entries.size();i < m;i++) { entries.get(i).init(this, getGui()); } updateScrollBar(); createArrow(); } public TreeComponent setColor(int color) { if(this.color != color) { this.color = color; onChanged(true); } return this; } public TreeComponent setHoverColor(int color) { hoverColor = color; return this; } public TreeComponent setSelectionColor(int color) { selectedColor = color; return this; } public TreeComponent setEntryHeight(float entryHeight) { if(this.entryHeight != entryHeight) { this.entryHeight = entryHeight; onChanged(true); } return this; } public TreeComponent setSelectionMode(int mode) { if(mode < 0 || mode > 3) { throw new IllegalStateException("Unknown Mode"); } this.selectionMode = mode; selectedNodes.clear(); return this; } public TreeComponent setUpdateMode(int mode) { if(mode < 0 || mode > 3) { throw new IllegalStateException("Unknown Mode"); } updateMode = mode; return this; } public TreeComponent disableBackground(boolean value) { setFlag(FLAG_NO_BACKGROUND, value); return this; } @Override protected void repaint() { float scale = getBox().getScale(); List entries = new ObjectArrayList(); getNodes(node, entries, false); for(int i = 0,m=entries.size();i setTree(T entry) { if(node == entry) { return this; } node = entry; if(entry != null) { entry.calculateDebth(); if(getGui() != null) { List entries = new ObjectArrayList(); getNodes(entry, entries, false); for(int i = 0,m=entries.size();i entries = new ObjectArrayList(); getNodes(node, entries, false); for(int i = 0,m=entries.size();i 0; } public T getSelectedNode() { return selectedNodes.isEmpty() ? null : selectedNodes.iterator().next(); } public List getSelectedNodes() { return new ObjectArrayList(this.selectedNodes); } public List getOpenNodes() { return new ObjectArrayList(this.visibleNodes); } public List getAllNodes() { List list = new ObjectArrayList(); getNodes(node, list, false); return list; } public void openAll() { if(node != null) { openNode(node, true); } } public void closeAll() { if(node != null) { closeNode(node, true); } } public void openNode(T entry, boolean childrenIncluded) { openNode(entry, childrenIncluded, Integer.MAX_VALUE); } public void openNode(T entry, boolean childrenIncluded, int maxLayers) { openNodes.add(entry); if(childrenIncluded) { getNodes(entry, openNodes, false, maxLayers); } listChange = true; updateScrollBar(); } public void toggleNode(T entry, boolean childrenIncluded) { if(isOpen(entry)) { closeNode(entry, childrenIncluded); return; } openNode(entry, childrenIncluded); } public boolean isOpen(T entry) { return openNodes.contains(entry); } public void closeNode(T entry, boolean childrenIncluded) { openNodes.remove(entry); if(childrenIncluded) { Set entries = new ObjectOpenHashSet(); getNodes(entry, entries, false); openNodes.removeAll(entries); } listChange = true; updateScrollBar(); } protected void createArrow() { float pixelSize = (entryHeight * 0.5F); UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.EAST); UIShapes.createArrow(buffer, pixelSize, pixelSize, color, Facing.SOUTH); } protected void updateScrollBar() { if(listChange) { listChange = false; visibleNodes.clear(); getNodes(node, visibleNodes, true); customButton = null; } float width = 0F; float pixelSize = getBox().getScale() * entryHeight; for(int i = 0,m=visibleNodes.size();i entries = new ObjectArrayList(); getNodes(node, entries, false); for(int i = 0,m=entries.size();i= visibleNodes.size() ? -1 : index; return true; } @Override protected boolean renderSelf(int mouseX, int mouseY, float particalTicks) { float brightness = getActiveBrightness(); UIRenderer render = getRenderer(); int start = getStartIndex(); int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); IGuiBox box = getBox(); float scale = box.getScale(); float minX = horizontalBar.getScroll() * scale; float minY = verticalBar.getScroll() * scale; float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); float pixelSize = scale * entryHeight; float offsetSize = pixelSize * 0.8F; if(isFlagNotSet(FLAG_NO_BACKGROUND)) { render.setBrightness(brightness).drawQuad(box, color); } render.push(); render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); boolean skip = false; if(hoverIndex != -1 && hoverIndex >= start && hoverIndex < end) { T node = visibleNodes.get(hoverIndex); float xOffset = (offsetSize + (node.getDebth() * offsetSize * 0.6F)) * 0.8F; if(!node.isLeaf() && mouseX - box.getMinX() >= xOffset - (pixelSize * 0.6F) && mouseX - box.getMinX() <= xOffset) { skip = true; } if(!skip) { int extraX = (int)(minX - getBox().getMinX() - xOffset); int extraY = (int)(minY - hoverIndex * pixelSize - getBox().getMinY()); if(node instanceof IButtonComponent && ((IButtonComponent)node).isComponentColliding(mouseX + extraX, mouseY + extraY)) { skip = true; } } float otherMax = node.getHighlightWidth() >= 0F ? xOffset + node.getHighlightWidth() : maxX; if(mouseX - box.getMinX() >= otherMax) skip = true; if(!skip && selectionMode != SELECTION_MODE_INTERACT) { float offset = pixelSize * hoverIndex; render.drawQuad(minX, offset, Math.min(otherMax, maxX), offset + pixelSize, 0.01F, hoverColor); } } mouseX -= box.getMinX(); mouseY -= box.getMinY(); if(selectedNodes.size() > 0) { render.setBrightness(0.75F * brightness); for(int i = start;i collector) { super.collectTooltips(mouseX, mouseY, particalTicks, collector); int start = getStartIndex(); int end = MathUtils.clamp(0, visibleNodes.size(), start + getIndexWidth()); mouseX -= getBox().getMinX(); mouseY -= getBox().getMinY(); float scale = getBox().getScale(); float minX = horizontalBar.getScroll() * scale; float minY = verticalBar.getScroll() * scale; float pixelSize = scale * entryHeight; float offsetSize = pixelSize * 0.8F; for(int i = start;i= offsetX - (entryHeight * scale * 0.6F) && mouseX - getBox().getMinX() <= offsetX) { toggleNode(visibleNodes.get(hoverIndex), Keyboard.isShiftDown()); return true; } if(visibleNodes.get(hoverIndex) instanceof IButtonComponent) { IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - offsetX); int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onClick(button, mouseX + extraX, mouseY + extraY)) { customButton = comp; return true; } } } dragIndex = hoverIndex; if(selectionMode == SELECTION_MODE_INTERACT) { dragIndex = -1; } lastMouse.set(mouseX, mouseY); return true; } @Override public boolean onDrag(int mouseX, int mouseY) { if(horizontalBar.onDrag(mouseX, mouseY) || verticalBar.onDrag(mouseX, mouseY)) { return true; } if(customButton != null) { float scale = getBox().getScale(); float offsetSize = entryHeight * scale * 0.8F; int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); return customButton.onDrag(mouseX + extraX, mouseY + extraY); } horizontalBar.addScroll(lastMouse.getX() - mouseX); verticalBar.addScroll(lastMouse.getY() - mouseY); movement += Math.abs(lastMouse.getX() - mouseX) + Math.abs(lastMouse.getY() - mouseY); lastMouse.set(mouseX, mouseY); return true; } @Override public void onRelease(int button, int mouseX, int mouseY) { horizontalBar.onRelease(button, mouseX, mouseY); verticalBar.onRelease(button, mouseX, mouseY); if(customButton != null && (hoverIndex >= 0 && hoverIndex < visibleNodes.size() && customButton == visibleNodes.get(hoverIndex))) { float scale = getBox().getScale(); float offsetSize = entryHeight * scale * 0.8F; int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); int extraY = (int)(verticalBar.getScroll() * scale - (hoverIndex * scale * entryHeight) - getBox().getMinY()); customButton.onRelease(button, mouseX + extraX, mouseY + extraY); customButton = null; } else if(dragIndex != -1 && dragIndex == hoverIndex && movement < 2) { if(isNodeSelected(visibleNodes.get(hoverIndex))) { removeSelectedNode(visibleNodes.get(hoverIndex)); } else { addSelectedNode(visibleNodes.get(hoverIndex)); } notifyListeners(LISTENER_USER_ACTION); dragIndex = -1; } movement = 0; } @Override public boolean onScroll(int scroll, int mouseX, int mouseY) { if((horizontalBar.isComponentColliding(mouseX, mouseY) && horizontalBar.onScroll(scroll, mouseX, mouseY)) || (verticalBar.isComponentColliding(mouseX, mouseY) && verticalBar.onScroll(scroll, mouseX, mouseY))) { movement = 100; return true; } if(hoverIndex != -1 && visibleNodes.get(hoverIndex) instanceof IButtonComponent) { IButtonComponent comp = (IButtonComponent)visibleNodes.get(hoverIndex); float scale = getBox().getScale(); float offsetSize = entryHeight * scale * 0.8F; int extraX = (int)((horizontalBar.getScroll() * scale - getBox().getMinX()) - ((offsetSize + (visibleNodes.get(hoverIndex).getDebth() * offsetSize * 0.6F)) * 0.8F)); int extraY = (int)(verticalBar.getScroll() - (hoverIndex * getBox().getScale() * entryHeight) - getBox().getMinY()); if(comp.isComponentColliding(mouseX + extraX, mouseY + extraY) && comp.onScroll(scroll, mouseX + extraX, mouseY + extraY)) { return true; } } if(verticalBar.isInUse()) { verticalBar.addScroll(-(int)(scroll * 5F * getBox().getScale())); return true; } return false; } public int getStartIndex() { return MathUtils.clamp(0, visibleNodes.size(), MathUtils.floor(verticalBar.getScroll() / entryHeight)); } public int getIndexWidth() { return MathUtils.clamp(0, visibleNodes.size(), MathUtils.ceil((getBox().getBaseHeight() - (horizontalBar.getRequiredSpace() / getBox().getScale())) / entryHeight) + 1); } protected void getNodes(T entry, Collection collection, boolean openOnly, int layers) { if(entry != null && layers >= 0) { collection.add(entry); if(!entry.isLeaf() && (!openOnly || openNodes.contains(entry))) { for(int i = 0,m=entry.getChildCount();i collection, boolean openOnly) { if(entry != null) { collection.add(entry); if(!entry.isLeaf() && (!openOnly || openNodes.contains(entry))) { for(int i = 0,m=entry.getChildCount();i