512 lines
13 KiB
Java
512 lines
13 KiB
Java
package speiger.src.coreengine.rendering.gui.base;
|
|
|
|
import java.util.Comparator;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.function.IntToLongFunction;
|
|
|
|
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectLinkedOpenHashMap;
|
|
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
|
|
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
|
|
import speiger.src.collections.ints.maps.interfaces.Int2ObjectOrderedMap;
|
|
import speiger.src.collections.ints.sets.IntSet;
|
|
import speiger.src.collections.objects.lists.ObjectArrayList;
|
|
import speiger.src.collections.objects.maps.impl.hash.Object2BooleanLinkedOpenHashMap;
|
|
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
|
|
import speiger.src.collections.objects.maps.interfaces.Object2BooleanOrderedMap;
|
|
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
|
|
import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
|
|
import speiger.src.collections.objects.sets.ObjectOrderedSet;
|
|
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
|
|
import speiger.src.coreengine.math.MathUtils;
|
|
import speiger.src.coreengine.math.misc.ColorUtils;
|
|
import speiger.src.coreengine.rendering.gui.GuiBase;
|
|
import speiger.src.coreengine.rendering.gui.GuiComponent;
|
|
import speiger.src.coreengine.rendering.gui.components.TooltipPanel;
|
|
import speiger.src.coreengine.rendering.gui.helper.constrains.Constrains;
|
|
import speiger.src.coreengine.rendering.gui.renderer.UIRenderer;
|
|
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;
|
|
|
|
public class GuiScreenBase extends GuiBase
|
|
{
|
|
Int2ObjectMap<GuiComponent> getters = new Int2ObjectOpenHashMap<>();
|
|
Set<GuiComponent> components = new ObjectLinkedOpenHashSet<>();
|
|
Set<IKeyComponent> keyOrder = new ObjectLinkedOpenHashSet<>();
|
|
ObjectOrderedSet<GuiComponent> renderOrder = new ObjectLinkedOpenHashSet<>();
|
|
Object2BooleanOrderedMap<IButtonComponent> buttonOrder = new Object2BooleanLinkedOpenHashMap<>();
|
|
Int2ObjectOrderedMap<IButtonComponent> selectedButtons = new Int2ObjectLinkedOpenHashMap<>();
|
|
Set<IButtonComponent> draggingButtons = new ObjectLinkedOpenHashSet<>();
|
|
TooltipPanel tooltips = new TooltipPanel();
|
|
int lastMouseX = -1;
|
|
int lastMouseY = -1;
|
|
long lastTooltipCheck = 0;
|
|
boolean drawsTooltip = false;
|
|
long delay = 200L;
|
|
|
|
@Override
|
|
public void onInit()
|
|
{
|
|
super.onInit();
|
|
addComponent(tooltips);
|
|
}
|
|
|
|
public void setDelay(long delay, TimeUnit unit)
|
|
{
|
|
this.delay = Math.max(0, unit.toMillis(delay));
|
|
}
|
|
|
|
@Override
|
|
public <T extends GuiComponent> T addComponent(T comp, Constrains contrains)
|
|
{
|
|
components.add(comp);
|
|
renderOrder.addAndMoveToLast(comp);
|
|
addConstrains(comp, contrains);
|
|
comp.setOwner(this);
|
|
if(comp instanceof IButtonComponent)
|
|
{
|
|
buttonOrder.put((IButtonComponent)comp, true);
|
|
addButtonListener((IButtonComponent)comp);
|
|
}
|
|
if(comp instanceof IKeyComponent)
|
|
{
|
|
addKeyListener((IKeyComponent)comp);
|
|
}
|
|
return comp;
|
|
}
|
|
|
|
@Override
|
|
public <T extends GuiComponent> T addComponent(int id, T comp, Constrains contrains)
|
|
{
|
|
getters.put(id, comp);
|
|
return addComponent(comp, contrains);
|
|
}
|
|
|
|
@Override
|
|
public void addButtonListener(IButtonComponent listener)
|
|
{
|
|
buttonOrder.putAndMoveToFirst(listener, false);
|
|
}
|
|
|
|
@Override
|
|
public void addKeyListener(IKeyComponent listener)
|
|
{
|
|
keyOrder.add(listener);
|
|
}
|
|
|
|
@Override
|
|
public GuiComponent getComponent(int id)
|
|
{
|
|
return getters.get(id);
|
|
}
|
|
|
|
@Override
|
|
public void removeComponent(GuiComponent comp)
|
|
{
|
|
if(comp != null)
|
|
{
|
|
comp.onClosed();
|
|
}
|
|
components.remove(comp);
|
|
renderOrder.remove(comp);
|
|
getters.values().remove(comp);
|
|
if(comp instanceof IButtonComponent)
|
|
{
|
|
removeButtonListener((IButtonComponent)comp);
|
|
}
|
|
if(comp instanceof IKeyComponent)
|
|
{
|
|
removeKeyListener((IKeyComponent)comp);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeButtonListener(IButtonComponent listener)
|
|
{
|
|
buttonOrder.rem(listener);
|
|
}
|
|
|
|
@Override
|
|
public void removeKeyListener(IKeyComponent listener)
|
|
{
|
|
keyOrder.remove(listener);
|
|
}
|
|
|
|
@Override
|
|
public void onClosed()
|
|
{
|
|
super.onClosed();
|
|
for(GuiComponent entry : components)
|
|
{
|
|
entry.onClosed();
|
|
}
|
|
getters.clear();
|
|
renderOrder.clear();
|
|
buttonOrder.clear();
|
|
}
|
|
|
|
@Override
|
|
public void onScreenChanged()
|
|
{
|
|
super.onScreenChanged();
|
|
for(GuiComponent entry : components)
|
|
{
|
|
entry.onChanged(true);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFixedUpdate()
|
|
{
|
|
for(GuiComponent entry : renderOrder)
|
|
{
|
|
if(entry.isVisible())
|
|
{
|
|
entry.fixedUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void update(int mouseX, int mouseY, float particalTicks)
|
|
{
|
|
for(GuiComponent entry : renderOrder)
|
|
{
|
|
if(entry.isVisible())
|
|
{
|
|
entry.update(mouseX, mouseY, particalTicks);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void render(int mouseX, int mouseY, float particalTicks)
|
|
{
|
|
layers = (1 + getBaseLayer());
|
|
UIRenderer render = getRenderer();
|
|
float biggestZ = 0.0F;
|
|
float extra = 1.0F;
|
|
Object2ObjectMap<UUID, GuiComponent> components = hasFocus() ? new Object2ObjectLinkedOpenHashMap<>() : Object2ObjectMaps.empty();
|
|
for(GuiComponent base : renderOrder)
|
|
{
|
|
if(base.isVisible() && !base.isManualRender())
|
|
{
|
|
float z = base.getZOffset();
|
|
boolean layer = base.usesRenderOrder();
|
|
render.translate(0.0F, 0.0F, layers + z + (layer ? extra : 0.0F));
|
|
base.render(mouseX, mouseY, particalTicks);
|
|
render.resetTransform();
|
|
biggestZ = Math.max(biggestZ, z);
|
|
if(layer)
|
|
{
|
|
extra += 1.0F;
|
|
}
|
|
}
|
|
if(hasFocus())
|
|
{
|
|
base.collectTooltips(mouseX, mouseY, particalTicks, components);
|
|
}
|
|
}
|
|
layers += MathUtils.floor(biggestZ + extra);
|
|
if(hasFocus())
|
|
{
|
|
if(!drawsTooltip && (lastMouseX != mouseX || lastMouseY != mouseY) || components.isEmpty())
|
|
{
|
|
lastTooltipCheck = System.currentTimeMillis();
|
|
lastMouseX = mouseX;
|
|
lastMouseY = mouseY;
|
|
if(components.isEmpty())
|
|
{
|
|
tooltips.updateTooltips(components);
|
|
drawsTooltip = false;
|
|
}
|
|
}
|
|
else if(System.currentTimeMillis() - lastTooltipCheck >= delay)
|
|
{
|
|
drawsTooltip = true;
|
|
tooltips.updateTooltips(components);
|
|
tooltips.set(mouseX+tooltips.isOutsideScreen(mouseX, width), mouseY);
|
|
render.translate(0.0F, 0.0F, layers + 50F);
|
|
tooltips.render(mouseX, mouseY, particalTicks);
|
|
render.resetTransform();
|
|
}
|
|
}
|
|
render.resetEffects();
|
|
}
|
|
|
|
public void drawBox(GuiComponent comp)
|
|
{
|
|
if(!getUIManager().isRenderUIBoxes()) return;
|
|
UIRenderer render = getRenderer();
|
|
render.translate(0F, 0F, 100F);
|
|
render.drawFrame(comp.getBox(), ColorUtils.RED);
|
|
render.translate(0F, 0F, -100F);
|
|
}
|
|
|
|
@Override
|
|
public boolean isAllowingMovement()
|
|
{
|
|
for(IKeyComponent comp : findKeyPopups())
|
|
{
|
|
if(comp.isAcceptingInput() && comp.isBlockingMovement())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyTyped(char letter, int codepoint)
|
|
{
|
|
for(IKeyComponent comp : findKeyPopups())
|
|
{
|
|
if(comp.isAcceptingInput() && comp.onKeyTyped(letter, codepoint))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyPressed(int key)
|
|
{
|
|
for(IKeyComponent comp : findKeyPopups())
|
|
{
|
|
if(comp.isAcceptingInput() && comp.onKeyPressed(key))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onMousePressed(int button, int mouseX, int mouseY)
|
|
{
|
|
if(buttonOrder.isEmpty()) return false;
|
|
List<IButtonComponent> components = new ObjectArrayList<>();
|
|
for(Iterator<IButtonComponent> iter = findPopups();iter.hasNext();)
|
|
{
|
|
IButtonComponent base = iter.next();
|
|
if(!base.isValidButton(button)) continue;
|
|
if(!base.isComponentColliding(mouseX, mouseY))
|
|
{
|
|
components.add(base);
|
|
continue;
|
|
}
|
|
if(!base.onClick(button, mouseX, mouseY)) continue;
|
|
buttonOrder.getAndMoveToFirst(base);
|
|
selectedButtons.put(button, base);
|
|
draggingButtons.add(base);
|
|
GuiComponent top = getTopComponent(base);
|
|
if(base.canMoveIntoForground())
|
|
{
|
|
if(buttonOrder.getAndMoveToFirst(base) && base instanceof GuiComponent)
|
|
{
|
|
requestComponentFocus(((GuiComponent)base).getTopComponent());
|
|
}
|
|
else
|
|
{
|
|
requestComponentFocus(top);
|
|
}
|
|
}
|
|
else if(top instanceof IButtonComponent && ((IButtonComponent)top).canMoveIntoForground())
|
|
{
|
|
requestComponentFocus(top);
|
|
}
|
|
for(int i = 0,m=components.size();i<m;i++)
|
|
{
|
|
components.get(i).onFocusLost();
|
|
}
|
|
while(iter.hasNext())
|
|
{
|
|
IButtonComponent com = iter.next();
|
|
if(com == null || com == base)
|
|
{
|
|
continue;
|
|
}
|
|
com.onFocusLost();
|
|
}
|
|
return true;
|
|
}
|
|
for(int i = 0,m=components.size();i<m;i++)
|
|
{
|
|
components.get(i).onFocusLost();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onMouseDragged(IntSet activeButtons, int mouseX, int mouseY, IntToLongFunction timeMap)
|
|
{
|
|
boolean result = false;
|
|
for(IButtonComponent button : draggingButtons)
|
|
{
|
|
result |= button.onDrag(mouseX, mouseY);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean onMouseReleased(int button, int mouseX, int mouseY)
|
|
{
|
|
IButtonComponent buttonObj = selectedButtons.remove(button);
|
|
if(buttonObj != null)
|
|
{
|
|
if(buttonObj.isComponentColliding(mouseX, mouseY))
|
|
{
|
|
buttonObj.onRelease(button, mouseX, mouseY);
|
|
draggingButtons.retainAll(selectedButtons.values());
|
|
return true;
|
|
}
|
|
buttonObj.onFocusLost();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onMouseScroll(int mouseX, int mouseY, int scrollX)
|
|
{
|
|
if(buttonOrder.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
for(IButtonComponent base : IterableWrapper.wrap(findPopups()))
|
|
{
|
|
if(!base.isComponentColliding(mouseX, mouseY))
|
|
{
|
|
continue;
|
|
}
|
|
if(base.onScroll(scrollX, mouseX, mouseY))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public Iterator<IButtonComponent> findPopups()
|
|
{
|
|
List<IButtonComponent> popups = new ObjectArrayList<IButtonComponent>();
|
|
for(IButtonComponent button : buttonOrder.keySet())
|
|
{
|
|
if(button.isPopup() && !button.hasChildPopups())
|
|
{
|
|
popups.add(button);
|
|
continue;
|
|
}
|
|
GuiComponent comp = getTopComponent(button);
|
|
if(comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup() && !((IButtonComponent)comp).hasChildPopups())
|
|
{
|
|
popups.add(button);
|
|
}
|
|
}
|
|
return sortByHeight(popups.isEmpty() ? new ObjectArrayList<IButtonComponent>(buttonOrder.keySet()) : popups);
|
|
}
|
|
|
|
protected Iterator<IButtonComponent> sortByHeight(List<IButtonComponent> comp)
|
|
{
|
|
comp.sort(ButtonSorter.SORTER);
|
|
return comp.iterator();
|
|
}
|
|
|
|
protected Set<IKeyComponent> findKeyPopups()
|
|
{
|
|
Set<IKeyComponent> components = new ObjectLinkedOpenHashSet<IKeyComponent>();
|
|
for(IKeyComponent key : keyOrder)
|
|
{
|
|
if(key.isPopup() && !key.hasChildPopups())
|
|
{
|
|
components.add(key);
|
|
continue;
|
|
}
|
|
GuiComponent comp = getTopComponent(key);
|
|
if((comp instanceof IKeyComponent && ((IKeyComponent)comp).isPopup() && !((IKeyComponent)comp).hasChildPopups()) || (comp instanceof IButtonComponent && ((IButtonComponent)comp).isPopup()) && !((IButtonComponent)comp).hasChildPopups())
|
|
{
|
|
components.add(key);
|
|
}
|
|
}
|
|
return components.isEmpty() ? keyOrder : components;
|
|
}
|
|
|
|
protected GuiComponent getTopComponent(IKeyComponent button)
|
|
{
|
|
return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null;
|
|
}
|
|
|
|
protected GuiComponent getTopComponent(IButtonComponent button)
|
|
{
|
|
return button instanceof GuiComponent ? ((GuiComponent)button).getTopComponent() : null;
|
|
}
|
|
|
|
@Override
|
|
public void requestComponentFocus(GuiComponent comp)
|
|
{
|
|
if(!comp.hasPopups())
|
|
{
|
|
renderOrder.moveToLast(comp);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasComponentInTheWay(GuiComponent comp, int mouseX, int mouseY)
|
|
{
|
|
for(GuiComponent other : renderOrder)
|
|
{
|
|
if(other == comp) break;
|
|
if(other.usesRenderOrder() && other.isHovered(mouseX, mouseY)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isComponentInWay(GuiComponent comp, int mouseX, int mouseY)
|
|
{
|
|
for(IButtonComponent entry : buttonOrder.keySet())
|
|
{
|
|
if(entry != comp && entry instanceof GuiComponent && ((GuiComponent)entry).isHovered(mouseX, mouseY))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isComponentFocused(GuiComponent comp)
|
|
{
|
|
return hasFocus() && buttonOrder.size() > 0 && ((buttonOrder.size() == 1 && buttonOrder.containsKey(comp)) || buttonOrder.firstKey() == comp);
|
|
}
|
|
|
|
@Override
|
|
public boolean isComponentInFront(GuiComponent comp)
|
|
{
|
|
if(!hasFocus() || renderOrder.last() == comp || (renderOrder.last() instanceof IButtonComponent && ((IButtonComponent)renderOrder.last()).isPopup()))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasComponentPressed(int mouseButton)
|
|
{
|
|
return selectedButtons.containsKey(mouseButton);
|
|
}
|
|
|
|
static final class ButtonSorter implements Comparator<IButtonComponent>
|
|
{
|
|
public static final ButtonSorter SORTER = new ButtonSorter();
|
|
@Override
|
|
public int compare(IButtonComponent o1, IButtonComponent o2)
|
|
{
|
|
return Float.compare(o2.getComponentZ(), o1.getComponentZ());
|
|
}
|
|
|
|
}
|
|
}
|