package speiger.src.coreengine.rendering.gui.components; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; import speiger.src.collections.ints.collections.IntCollection; import speiger.src.collections.ints.lists.IntArrayList; import speiger.src.collections.ints.lists.IntList; import speiger.src.collections.ints.sets.IntLinkedOpenHashSet; import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.lists.ObjectList; import speiger.src.collections.objects.utils.ObjectIterators; import speiger.src.coreengine.math.MathUtils; import speiger.src.coreengine.math.misc.ColorUtils; 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.list.IListEntry; 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.input.Keyboard; public class ListComponent extends GuiComponent implements Iterable, 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_ALL = 2; public static final int FLAG_DISABLE_BACKGROUND = 1024; public static final int FLAG_START_AT_BOTTOM = 2048; protected int color; protected int hoverColor = ColorUtils.LIGHT_GRAY; protected int hoverIndex = -1; protected int dragIndex = -1; protected int movement = 0; protected IButtonComponent customButton; protected IntLinkedOpenHashSet selectedIndexes = new IntLinkedOpenHashSet(); protected ObjectList entries = new ObjectArrayList(); protected int selectionMode = 1; protected int updateMode = 1; protected float entryHeight; protected float cachedWidth = 0F; protected ScrollBarComponent verticalBar = new ScrollBarComponent(ColorUtils.LIGHT_GRAY); protected ScrollBarComponent horizontalBar = new ScrollBarComponent(ColorUtils.LIGHT_GRAY).setHorizontal(true); protected Vec2i lastMouse = Vec2i.newMutable(); public ListComponent() { super(0F, 0F, 0F, 0F); } public ListComponent(int color, float entryHeight) { super(0F, 0F, 0F, 0F); this.color = color; this.entryHeight = entryHeight; } public ListComponent(float x, float y, float width, float height, int color, float entryHeight) { super(x, y, width, height); this.color = color; this.entryHeight = entryHeight; } @Override public void init() { addChild(horizontalBar, Constraints.getScrollbarConstraints(verticalBar::isInUse, true, 5F)); addChild(verticalBar, Constraints.getScrollbarConstraints(horizontalBar::isInUse, false, 5F)); for(int i = 0, m = entries.size();i < m;i++) { entries.get(i).init(this, getGui()); } updateScrollBar(); } @Override public void onClosed() { for(int i = 0,m=entries.size();i disableBackground() { setFlag(FLAG_DISABLE_BACKGROUND); return this; } public ListComponent setStartAtBottom(boolean value) { setFlag(FLAG_START_AT_BOTTOM, value); return this; } public boolean isStartAtBottom() { return isFlagSet(FLAG_START_AT_BOTTOM); } public ListComponent setColor(int color) { this.color = color; return this; } public ListComponent setHoverColor(int color) { hoverColor = color; return this; } public ListComponent setEntryHeight(float entryHeight) { if(this.entryHeight != entryHeight) { this.entryHeight = entryHeight; onChanged(true); } return this; } public ListComponent setSelectionMode(int mode) { if(mode < 0 || mode > 3) { throw new IllegalStateException("Unknown Mode"); } this.selectionMode = mode; selectedIndexes.clear(); return this; } public ListComponent setUpdateMode(int mode) { if(mode < 0 || mode > 2) { throw new IllegalStateException("Unknown Mode"); } updateMode = mode; return this; } public ListComponent setSelectedIndex(int index) { switch(selectionMode) { case 0: throw new IllegalStateException("Selection is not allowed"); case 1: selectedIndexes.clear(); case 2: if(index >= 0 && index < entries.size()) { if(selectionMode == SELECTION_MODE_MULTI && Keyboard.isShiftDown()) { for(int i = selectedIndexes.lastInt();iindex;i--) { selectedIndexes.add(i); } } selectedIndexes.add(index); } break; } return this; } public boolean isSelected(int index) { return selectedIndexes.contains(index); } public T removeSelectedIndex(int index) { return selectedIndexes.remove(index) ? entries.get(index) : null; } public boolean hasSelected() { return !selectedIndexes.isEmpty(); } public int getSelectedIndex() { return selectedIndexes.isEmpty() ? -1 : selectedIndexes.iterator().nextInt(); } public T getSelectedValue() { return selectedIndexes.isEmpty() ? null : entries.get(selectedIndexes.iterator().nextInt()); } public void clearSelection() { selectedIndexes.clear(); } public IntList getSelection() { return new IntArrayList(selectedIndexes); } public List getSelectedValues() { List entries = new ObjectArrayList(); for(int index : selectedIndexes) { entries.add(this.entries.get(index)); } return entries; } public ListComponent add(T entry) { entries.add(entry); if(getGui() != null) { entry.init(this, getGui()); updateScrollBar(); } return this; } public ListComponent addAll(Collection entries) { this.entries.addAll(entries); if(getGui() != null) { for(T entry : entries) { entry.init(this, getGui()); } updateScrollBar(); } return this; } public int size() { return entries.size(); } public T get(int index) { return entries.get(index); } public boolean removeIf(Predicate filter) { if(entries.isEmpty()) return false; selectedIndexes.clear(); return entries.removeIf(K -> { if(filter.test(K)) { K.onClosed(); return true; } return false; }); } public List map(Function mapper) { List list = new ObjectArrayList<>(entries.size()); for(int i = 0,m=entries.size();i 0) { entries.add(index - 1, entries.remove(index)); if(isSelected(index)) { removeSelectedIndex(index); setSelectedIndex(index-1); } } } public void moveDown(int index) { if(index + 1 < entries.size()) { entries.add(index + 1, entries.remove(index)); if(isSelected(index)) { removeSelectedIndex(index); setSelectedIndex(index+1); } } } public void move(int fromIndex, int toIndex) { entries.add(toIndex, entries.remove(fromIndex)); } public int indexOf(T value) { return entries.indexOf(value); } public T remove(int index) { if(index < 0 || index >= entries.size()) { return null; } T value = entries.remove(index); if(value != null) { value.onClosed(); selectedIndexes.remove(index); } return value; } public boolean removeAll(IntCollection values) { List result = new ObjectArrayList(); for(int index : values) { if(index < 0 || index >= entries.size()) { continue; } selectedIndexes.remove(index); T entry = entries.get(index); entry.onClosed(); result.add(entry); } if(result.size() > 0) { entries.removeAll(result); return true; } return false; } public boolean remove(T value) { return remove(indexOf(value)) != null; } public void clear() { for(int i = 0,m=entries.size();i iterator() { return ObjectIterators.unmodifiable(entries.iterator()); } protected Iterator rangeIterator(int start, int end) { return new Iterator() { int index = start; @Override public boolean hasNext() { return index < end && entries.size() > index; } @Override public T next() { return entries.get(index++); } }; } public Iterator visibleIterator() { int start = getStartIndex(); return rangeIterator(start, MathUtils.clamp(0, entries.size() - 1, start + getIndexWidth())); } public float getCachedWidth() { return cachedWidth; } @Override protected void repaint() { float scale = getBox().getScale(); for(int i = 0,m=entries.size();i= entries.size() ? -1 : index; return true; } @Override public boolean renderSelf(int mouseX, int mouseY, float particalTicks) { float brightness = getActiveBrightness(); UIRenderer render = getRenderer(); int start = getStartIndex(); int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); IGuiBox box = getBox(); float scale = box.getScale(); float pixelSize = scale * entryHeight; render.setBrightness(brightness); if(isFlagNotSet(FLAG_DISABLE_BACKGROUND)) { render.drawQuad(box, color); } render.push(); render.translate(box.getMinX(-horizontalBar.getScroll()), box.getMinY(-verticalBar.getScroll())); float minX = horizontalBar.getScroll() * scale; float maxX = box.getWidth() - verticalBar.getRequiredSpace() + (horizontalBar.getScroll() * scale); enableScissorsBox(box.getMinX(), box.getMinY(), box.getMaxX()-verticalBar.getRequiredSpace(), box.getMaxY()-horizontalBar.getRequiredSpace()); boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); if(hoverIndex != -1 && selectionMode != SELECTION_MODE_INTERACT && hoverIndex >= start && hoverIndex < end) { float offset = bottom ? box.getHeight()-(end-hoverIndex) * pixelSize : hoverIndex * pixelSize; render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.01F, hoverColor); } if(selectedIndexes.size() > 0) { render.setBrightness(0.75F * brightness); for(int index : selectedIndexes) { if(index >= start && index < end) { float offset = bottom ? box.getHeight()-(end-index) * pixelSize : index * pixelSize; render.drawQuad(minX, offset, maxX, offset + pixelSize, 0.011F, color); } } } mouseX -= box.getMinX(); mouseY -= box.getMinY(); for(int i = start;i collector) { super.collectTooltips(mouseX, mouseY, particalTicks, collector); int start = getStartIndex(); int end = MathUtils.clamp(0, entries.size(), start + getIndexWidth()); IGuiBox box = getBox(); float scale = box.getScale(); float pixelSize = scale * entryHeight; mouseX -= box.getMinX(); mouseY -= box.getMinY(); boolean bottom = !verticalBar.isInUse() && isStartAtBottom(); for(int i = start;i