357 lines
10 KiB
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);
|
|
}
|
|
}
|
|
}
|