SimpleJavaEngine/src/main/java/speiger/src/coreengine/rendering/gui/components/WindowComponent.java

357 lines
10 KiB
Java

package speiger.src.coreengine.rendering.gui.components;
import java.util.function.Consumer;
import java.util.function.ObjIntConsumer;
import speiger.src.coreengine.math.misc.ColorUtils;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.math.misc.FacingList;
import speiger.src.coreengine.math.value.IValue;
import speiger.src.coreengine.math.value.LiniarValue;
import speiger.src.coreengine.math.vector.floats.Vec2f;
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.icon.CrossIcon;
import speiger.src.coreengine.rendering.gui.components.icon.LineIcon;
import speiger.src.coreengine.rendering.gui.helper.Align;
import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.gui.helper.constrains.Constrains;
import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain;
import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.utils.functions.ConsumerConverter;
import speiger.src.coreengine.utils.helpers.InternalThreadPools;
public class WindowComponent extends PanelComponent implements IButtonComponent, ObjIntConsumer<GuiComponent>
{
public static final Vec2f DEFAULT_MINIMUM_BOUNDS = Vec2f.of(75F, 7.5F);
public static final int FLAG_MINIMIZED = 1 << 20;
public static final int FLAG_RESIZEABLE = 1 << 21;
public static final int FLAG_MOVEABLE = 1 << 22;
public static final int FLAG_RESIZEABLE_HORIZONTAL = 1 << 23;
public static final int FLAG_RESIZEABLE_VERTICAL = 1 << 24;
public static final int FLAG_RESIZE_INVERT = 1 << 25;
public static final int WINDOW_FLAG_CLOSEABLE = 1;
public static final int WINDOW_FLAG_MINIMIZEABLE = 2;
public static final int WINDOW_FLAGS = WINDOW_FLAG_CLOSEABLE | WINDOW_FLAG_MINIMIZEABLE;
public static final int DEFAULT_FLAGS = WINDOW_FLAGS | FLAG_RESIZEABLE | FLAG_MOVEABLE;
public static final int FIXED_SIZE_WINDOW = WINDOW_FLAGS | FLAG_MOVEABLE;
public static final int FIXED_SIZE_POPUP = WINDOW_FLAG_CLOSEABLE | FLAG_MOVEABLE;
public static final int DYNAMIC_POPUP = FIXED_SIZE_POPUP | FLAG_RESIZEABLE;
public static final int UNCLOSEABLE_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_MOVEABLE;
public static final int SUB_WINDOW = WINDOW_FLAG_MINIMIZEABLE | FLAG_RESIZEABLE | FLAG_RESIZE_INVERT;
final int flags;
FacingList facing = null;
String name;
int color = ColorUtils.WINDOW_DEFAULT_BACKGROUND;
Vec2f lastSize = Vec2f.mutable();
Vec2i lastClick = Vec2i.mutable();
IValue animation = null;
protected final Consumer<GuiComponent> closeListener = new ConsumerConverter<GuiComponent>(0, this);
protected final Consumer<GuiComponent> minimizedListener = new ConsumerConverter<GuiComponent>(2, this);
public WindowComponent(float x, float y, float width, float height, int flags, String name)
{
super(x, y, width, height);
this.name = name;
this.flags = flags & WINDOW_FLAGS;
setFlag(flags &= ~(WINDOW_FLAGS));
setFlag(FLAG_RESIZEABLE_HORIZONTAL | FLAG_RESIZEABLE_VERTICAL);
lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight());
}
@Override
public void init()
{
super.init();
LabelComponent label = new LabelComponent(name, ColorUtils.DARK_GRAY);
label.getText().setTextScale(0.4F).horizontal(Align.LEFT_TOP).singleLine(true);
addChild(label, new Constrains(null, null, new ParentConstrain(), new PixelConstrain(7.5F)));
float offset = 9F;
if((flags & WINDOW_FLAG_CLOSEABLE) != 0)
{
addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorUtils.RED, new CrossIcon(ColorUtils.WHITE).setPadding(2.5F, 2F)).addUserActionListener(new ConsumerConverter<>(0, this)).setZOffset(0.001F), new Constrains(new PixelConstrain(offset).setInverted(), null, null, null));
offset += 7.5F;
}
if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0)
{
addChild(new IconButtonComponent(0F, 0F, 7.5F, 7.5F, ColorUtils.GRAY, new LineIcon(ColorUtils.WHITE, 0.7F, 0.25F)).addUserActionListener(new ConsumerConverter<>(1, this)).setZOffset(0.001F), new Constrains(new PixelConstrain(offset).setInverted(), null, null, null));
}
if(canMoveIntoForground())
{
setFlag(FLAG_RENDER_ORDER);
}
}
protected void updateMinizedState(boolean value)
{
if(setFlag(FLAG_MINIMIZED, value))
{
onChanged(false);
}
}
public final WindowComponent setMinimized(boolean value)
{
if((flags & WINDOW_FLAG_MINIMIZEABLE) != 0 && setFlag(FLAG_MINIMIZED, value))
{
if(value)
{
Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize);
bounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY());
lastSize.set(last);
InternalThreadPools.VEC2F.accept(last.negate());
}
else bounds(lastSize.getX(), lastSize.getY());
onChanged(false);
}
return this;
}
public final boolean isMinimized()
{
return isFlagSet(FLAG_MINIMIZED);
}
public Vec2f getMinimumBounds()
{
return DEFAULT_MINIMUM_BOUNDS;
}
public float getMinimizedY()
{
return 7.5F;
}
public WindowComponent setColor(int color)
{
this.color = color;
return this;
}
@Override
public void calculateActualBounds(float[] area, boolean start)
{
if(animation != null)
{
float scale = getBox().getScale();
area[0] = Math.min(area[0], getBox().getMinX());
area[1] = Math.min(area[1], getBox().getMinY());
area[2] = Math.max(area[2], lastSize.getX() * scale);
area[3] = Math.max(area[3], animation.get(getMinimizedY(), lastSize.getY()) * scale);
return;
}
super.calculateActualBounds(area, start);
}
@Override
protected void updateState()
{
if(animation == null)
{
if(isFlagSet(FLAG_MINIMIZED))
{
lastSize.setX(getBox().getBaseWidth());
return;
}
lastSize.set(getBox().getBaseWidth(), getBox().getBaseHeight());
}
}
@Override
protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
{
if(animation != null)
{
animation.update(particalTicks);
if(animation.isDone())
{
if(animation.get() < 0.1F)
{
updateMinizedState(true);
}
Vec2f last = InternalThreadPools.VEC2F.get().set(lastSize);
bounds(last.getX(), isFlagSet(FLAG_MINIMIZED) ? getMinimizedY() : last.getY());
lastSize.set(last);
InternalThreadPools.VEC2F.accept(last.negate());
animation = null;
}
notifyListeners(LISTENER_ON_CHANGE);
}
else if(isFlagSet(FLAG_RESIZEABLE) && !isOverChild(mouseX, mouseY) && !getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY))
{
FacingList list = getBox().isColiding(mouseX, mouseY) ? getBox().getColidingBorder(mouseX, mouseY, 2F) : null;
if(list != null)
{
if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) list = list.remove(FacingList.HORIZONTAL);
if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) list = list.remove(FacingList.VERTICAL);
bindCursor(list.containsAny(FacingList.VERTICAL) && isFlagNotSet(FLAG_MINIMIZED) ? Cursor.CURSOR_VRESIZE : (list.containsAny(FacingList.HORIZONTAL) ? Cursor.CURSOR_HRESIZE : null));
}
}
if(isPopup() && !hasChildPopups() && !getGui().isComponentInFront(this))
{
requestFocus();
}
return true;
}
@Override
protected void preRender()
{
if(animation != null)
{
float scale = getBox().getScale();
enableScissors(getBox().getMinX(), getBox().getMinY(), lastSize.getX() * scale, animation.get(getMinimizedY(), lastSize.getY()) * scale);
}
}
@Override
protected boolean renderSelf(int mouseX, int mouseY, float particalTicks)
{
getRenderer().drawQuad(getBox(), color);
return true;
}
@Override
protected void postRender()
{
if(animation != null)
{
disableScissors();
}
}
@Override
public void accept(GuiComponent value, int index)
{
switch(index)
{
case 0:
getGui().removeComponent(this);
break;
case 1:
if(animation != null || (flags & WINDOW_FLAG_MINIMIZEABLE) == 0)
{
break;
}
animation = (isMinimized() ? new LiniarValue(1F, 0F, 1F) : new LiniarValue(1F, 1F, 0F)).setSmooth();
if(isMinimized())
{
bounds(lastSize.getX(), lastSize.getY());
}
updateMinizedState(false);
break;
case 2:
value.setVisible(!isMinimized());
break;
}
}
@Override
public boolean onClick(int button, int mouseX, int mouseY)
{
if(isOverChild(mouseX, mouseY) || getGui().hasComponentInTheWay(getTopComponent(), mouseX, mouseY))
{
return false;
}
facing = getBox().getColidingBorder(mouseX, mouseY, 2F);
lastClick.set(mouseX, mouseY);
if(facing != null)
{
if(isFlagNotSet(FLAG_RESIZEABLE_HORIZONTAL)) facing = facing.remove(FacingList.HORIZONTAL);
if(isFlagNotSet(FLAG_RESIZEABLE_VERTICAL)) facing = facing.remove(FacingList.VERTICAL);
}
return true;
}
@Override
public boolean onDrag(int mouseX, int mouseY)
{
if(facing != null)
{
if(facing.isEmpty() && isFlagSet(FLAG_MOVEABLE))
{
move(mouseX - lastClick.getX(), mouseY - lastClick.getY());
}
else if(!facing.isEmpty() && isFlagSet(FLAG_RESIZEABLE))
{
float scale = getBox().getScale();
float xChange = (mouseX - lastClick.getX()) * (facing.containsAny(FacingList.HORIZONTAL) ? 1F : 0F);
float yChange = (mouseY - lastClick.getY()) * (facing.containsAny(FacingList.VERTICAL) ? 1F : 0F);
if(isFlagSet(FLAG_RESIZE_INVERT))
{
xChange *= -1F;
yChange *= -1F;
}
setMassChanging();
if(facing.contains(Facing.NORTH) && isFlagNotSet(FLAG_MINIMIZED))
{
resize(0F, -(yChange / scale));
move(0F, yChange);
}
else if(facing.contains(Facing.SOUTH) && isFlagNotSet(FLAG_MINIMIZED))
{
resize(0F, yChange / scale);
}
if(facing.contains(Facing.WEST))
{
resize(-(xChange / scale) - 1F, 0F);
move(xChange, 0F);
}
else if(facing.contains(Facing.EAST))
{
resize(xChange / scale, 0F);
}
ensureMinimumBounds();
finishMassChanging();
if(xChange > 0F || yChange > 0F)
{
onChanged(true);
}
}
lastClick.set(mouseX, mouseY);
return true;
}
return false;
}
@Override
public void onRelease(int button, int mouseX, int mouseY)
{
facing = null;
}
@Override
public boolean canMoveIntoForground()
{
return true;
}
@Override
public void onFocusLost()
{
facing = null;
}
protected void ensureMinimumBounds()
{
IGuiBox box = getBox();
Vec2f bounds = getMinimumBounds();
if(box.getBaseWidth() < bounds.getX())
{
box.setWidth(bounds.getX());
onChanged(true);
}
if(box.getBaseHeight() < bounds.getY())
{
box.setHeight(bounds.getY());
onChanged(true);
}
}
}