Compare commits
19 Commits
0a7da1a7a9
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ad0ebe3fe4 | |||
| 22670c2468 | |||
| a9abe3c640 | |||
| 6416a55904 | |||
| 65cfff172d | |||
| 16e171a55e | |||
| eada8b87c3 | |||
| 2b6787143a | |||
| d9794cce22 | |||
| 3b8df5824f | |||
| 0e6a327c08 | |||
| 933ff6006f | |||
| fbcfad2346 | |||
| bc24b5dc09 | |||
| 8f9aeb2608 | |||
| 95bf1695ef | |||
| 280f294b57 | |||
| 412e3adf2a | |||
| 367f37e6f8 |
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Bootloader" type="Application" factoryName="Application" nameIsGenerated="true">
|
||||
<option name="MAIN_CLASS_NAME" value="be.seeseemelk.diceos.Bootloader" />
|
||||
<module name="diceos.main" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="be.seeseemelk.diceos.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
+8
-4
@@ -7,10 +7,14 @@ plugins {
|
||||
group = "be.seeseemelk"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
val gdxVersion = "1.14.0"
|
||||
val gdxVersion = "1.14.2"
|
||||
val slf4j = "2.0.18"
|
||||
val lombokVersion = "1.18.46"
|
||||
val avajeInjectVersion = "12.5"
|
||||
val avajeInjectVersion = "12.6-javax"
|
||||
|
||||
application {
|
||||
mainClass = "be.seeseemelk.diceos.Bootloader"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -35,14 +39,14 @@ dependencies {
|
||||
testAnnotationProcessor("io.avaje:avaje-inject-generator:${avajeInjectVersion}")
|
||||
|
||||
// Parsing TOML files
|
||||
implementation("tools.jackson.dataformat:jackson-dataformat-toml:3.1.3")
|
||||
implementation("tools.jackson.dataformat:jackson-dataformat-toml:3.2.0")
|
||||
|
||||
// LibGDX
|
||||
api("com.badlogicgames.gdx:gdx:$gdxVersion")
|
||||
api("com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion")
|
||||
api("com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:6.0.3"))
|
||||
testImplementation(platform("org.junit:junit-bom:6.1.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public class DiceOS {
|
||||
private static DiceOS INSTANCE;
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final WindowService windowService;
|
||||
private final InputService inputService;
|
||||
|
||||
@PostConstruct
|
||||
void register() {
|
||||
@@ -33,4 +34,8 @@ public class DiceOS {
|
||||
public static WindowService getWindowService() {
|
||||
return INSTANCE.windowService;
|
||||
}
|
||||
|
||||
public static InputService getInputService() {
|
||||
return INSTANCE.inputService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.ScreenUtils;
|
||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||
@@ -24,6 +23,7 @@ public class DiceOSAdapter extends ApplicationAdapter {
|
||||
private final DisplayService display;
|
||||
private final CursorService cursorService;
|
||||
private final WindowService windowService;
|
||||
private final InputService inputService;
|
||||
private final List<OnStartup> startupTasks;
|
||||
private Texture clouds;
|
||||
private Texture border;
|
||||
@@ -34,8 +34,6 @@ public class DiceOSAdapter extends ApplicationAdapter {
|
||||
private int offsetY = 0;
|
||||
private GraphicsContext gc;
|
||||
|
||||
private BitmapFont font;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
log.info("DiceOS starting...");
|
||||
@@ -50,8 +48,6 @@ public class DiceOSAdapter extends ApplicationAdapter {
|
||||
task.onStartup();
|
||||
}
|
||||
|
||||
font = resourceLoader.loadFont("system/font");
|
||||
|
||||
log.info("DiceOS started!");
|
||||
}
|
||||
|
||||
@@ -66,8 +62,11 @@ public class DiceOSAdapter extends ApplicationAdapter {
|
||||
offsetY = (height - (displayHeight * scaling)) / 2;
|
||||
|
||||
cursorService.setScale(scaling);
|
||||
inputService.setScale(scaling);
|
||||
inputService.setOffset(offsetX, offsetY);
|
||||
|
||||
gc = new GraphicsContext(screenViewport.getCamera(), display.getBatch(), displayHeight, displayWidth, font);
|
||||
gc = new GraphicsContext(screenViewport.getCamera(), display.getBatch(), displayHeight, displayWidth,
|
||||
DiceOS.getResourceLoader().getDefaultFont().getBitmapFont());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package be.seeseemelk.diceos.system;
|
||||
|
||||
import be.seeseemelk.diceos.system.toolkit.events.MouseClickEvent;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.InputAdapter;
|
||||
@@ -12,6 +13,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RequiredArgsConstructor
|
||||
public class InputService implements OnStartup {
|
||||
private final DisplayService displayService;
|
||||
private int scale = 1;
|
||||
private int offsetX = 0;
|
||||
private int offsetY = 0;
|
||||
|
||||
@Override
|
||||
public void onStartup() {
|
||||
@@ -24,6 +28,19 @@ public class InputService implements OnStartup {
|
||||
} else if (keycode == Input.Keys.ESCAPE) {
|
||||
Gdx.app.exit();
|
||||
}
|
||||
//DiceOS.getWindowService().onEvent(new KeyEvent(keycode));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
|
||||
DiceOS.getWindowService().onEvent(new MouseClickEvent(screenX, screenY, MouseClickEvent.Button.LEFT, MouseClickEvent.Action.RELEASED));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
|
||||
DiceOS.getWindowService().onEvent(new MouseClickEvent(screenX, screenY, MouseClickEvent.Button.LEFT, MouseClickEvent.Action.PRESSED));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -33,4 +50,36 @@ public class InputService implements OnStartup {
|
||||
return Gdx.input.isKeyPressed(com.badlogic.gdx.Input.Keys.ALT_LEFT) ||
|
||||
Gdx.input.isKeyPressed(com.badlogic.gdx.Input.Keys.ALT_RIGHT);
|
||||
}
|
||||
|
||||
public int getMouseX() {
|
||||
return (Gdx.input.getX() - offsetX) / scale;
|
||||
}
|
||||
|
||||
public int getMouseY() {
|
||||
return (Gdx.input.getY() - offsetY) / scale;
|
||||
}
|
||||
|
||||
void setScale(int scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
void setOffset(int x, int y) {
|
||||
this.offsetX = x;
|
||||
this.offsetY = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the mouse is currently within the specified rectangle.
|
||||
* @param x The x-coordinate of the rectangle.
|
||||
* @param y The y-coordinate of the rectangle.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The height of the rectangle.
|
||||
* @return True if the mouse is within the rectangle, false otherwise.
|
||||
*/
|
||||
public boolean mouseInBounds(int x, int y, int width, int height) {
|
||||
int mx = getMouseX();
|
||||
int my = getMouseY();
|
||||
return mx >= x && mx < x + width &&
|
||||
my >= y && my < y + height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package be.seeseemelk.diceos.system;
|
||||
|
||||
import be.seeseemelk.diceos.system.font.Font;
|
||||
import be.seeseemelk.diceos.system.font.FontLoader;
|
||||
import be.seeseemelk.diceos.system.font.FontMetadata;
|
||||
import be.seeseemelk.diceos.system.gfx.NinePatchLoader;
|
||||
@@ -18,6 +19,14 @@ import tools.jackson.dataformat.toml.TomlFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A utility class responsible for loading and caching graphical and textual assets used in the application.
|
||||
* It manages various types of resources including pixmaps, textures, nine-patches, and fonts, ensuring that
|
||||
* assets are loaded only once and subsequently reused to optimize performance.
|
||||
*
|
||||
* The class supports loading assets from specified file paths and caches them in memory. It implements a
|
||||
* lazy-loading mechanism to defer loading until a resource is requested.
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@@ -25,7 +34,7 @@ public class ResourceLoader {
|
||||
private final Map<String, Pixmap> pixmaps = new HashMap<>();
|
||||
private final Map<String, Texture> textures = new HashMap<>();
|
||||
private final Map<String, NinePatch> ninePatches = new HashMap<>();
|
||||
private final Map<String, BitmapFont> fonts = new HashMap<>();
|
||||
private final Map<String, Font> fonts = new HashMap<>();
|
||||
private final ObjectMapper mapper = new ObjectMapper(new TomlFactory());
|
||||
|
||||
public Pixmap loadPixmap(String path) {
|
||||
@@ -36,6 +45,13 @@ public class ResourceLoader {
|
||||
return textures.computeIfAbsent(path, p -> new Texture(Gdx.files.internal(p)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and retrieves a {@link NinePatch} object for the given image path. The method caches loaded
|
||||
* {@link NinePatch} instances to avoid redundant processing of the same asset.
|
||||
*
|
||||
* @param path the file path of the image to load as a 9-patch
|
||||
* @return the loaded {@link NinePatch} instance corresponding to the specified image path
|
||||
*/
|
||||
public NinePatch loadNinePatch(String path) {
|
||||
return ninePatches.computeIfAbsent(path, p -> {
|
||||
var pixmap = loadPixmap(p);
|
||||
@@ -44,16 +60,28 @@ public class ResourceLoader {
|
||||
});
|
||||
}
|
||||
|
||||
public BitmapFont loadFont(String path) {
|
||||
/**
|
||||
* Loads and retrieves a {@link Font} object for the given file path.
|
||||
* The method caches loaded {@link Font} instances to avoid redundant
|
||||
* processing of the same asset.
|
||||
*
|
||||
* @param path the file path of the font to load
|
||||
* @return the loaded {@link Font} instance corresponding to the specified file path
|
||||
*/
|
||||
public Font loadFont(String path) {
|
||||
return fonts.computeIfAbsent(path, this::loadBitmapfont);
|
||||
}
|
||||
|
||||
private BitmapFont loadBitmapfont(String path) {
|
||||
public Font getDefaultFont() {
|
||||
return loadFont("system/font");
|
||||
}
|
||||
|
||||
private Font loadBitmapfont(String path) {
|
||||
log.info("Loading bitmap font from {}", path);
|
||||
|
||||
var metadataString = Gdx.files.internal(path + ".toml").readString();
|
||||
var metadata = mapper.readValue(metadataString, FontMetadata.class);
|
||||
@Cleanup("dispose") var pixmap = loadPixmap(metadata.getGeneral().getSource());
|
||||
return FontLoader.loadBitmapfont(pixmap, metadata);
|
||||
return new Font(FontLoader.loadBitmapfont(pixmap, metadata));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,18 @@ package be.seeseemelk.diceos.system;
|
||||
|
||||
import be.seeseemelk.diceos.system.gfx.GraphicsContext;
|
||||
import be.seeseemelk.diceos.system.toolkit.Window;
|
||||
import be.seeseemelk.diceos.system.toolkit.events.Event;
|
||||
import be.seeseemelk.diceos.system.toolkit.events.MouseClickEvent;
|
||||
import be.seeseemelk.diceos.system.toolkit.menu.Menu;
|
||||
import com.badlogic.gdx.graphics.g2d.NinePatch;
|
||||
import io.avaje.inject.Component;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class WindowService implements OnStartup {
|
||||
@@ -65,11 +69,27 @@ public class WindowService implements OnStartup {
|
||||
|
||||
// Render menubar items
|
||||
gc.save();
|
||||
gc.translate(5, 0);
|
||||
gc.translate(8, 0);
|
||||
for (var submenu : menu.getItems()) {
|
||||
gc.save();
|
||||
gc.clip(0, 0, submenu.getWidth(), submenu.getHeight());
|
||||
submenu.paint(gc);
|
||||
gc.restore();
|
||||
gc.translate(submenu.getWidth(), 0);
|
||||
}
|
||||
gc.restore();
|
||||
}
|
||||
|
||||
void onEvent(Event event) {
|
||||
switch (event) {
|
||||
case MouseClickEvent e -> onMouseClickEvent(e);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private void onMouseClickEvent(MouseClickEvent event) {
|
||||
if (event.getY() <= 14) {
|
||||
log.info("Clicking on menubar");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package be.seeseemelk.diceos.system.font;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Represents a font used for rendering text, providing utilities
|
||||
* for measuring text dimensions.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class Font {
|
||||
@Getter
|
||||
private final BitmapFont bitmapFont;
|
||||
|
||||
/**
|
||||
* Calculates the width of the given text when rendered with the current font.
|
||||
* @param text The text to measure.
|
||||
* @return The width of the text in pixels.
|
||||
*/
|
||||
public int getTextWidth(String text) {
|
||||
var layout = new com.badlogic.gdx.graphics.g2d.GlyphLayout();
|
||||
layout.setText(bitmapFont, text);
|
||||
return (int) layout.width;
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,7 @@ public class FontLoader {
|
||||
if (!isBlack) {
|
||||
currentTiming.glyphEnd = x;
|
||||
state = State.WAITING_FOR_GLYPH_WIDTHS;
|
||||
log.info("Found glyph width: {} (from {} to {})", currentTiming.getGlyphWidth(), currentTiming.glyphStart, currentTiming.glyphEnd);
|
||||
//log.info("Found glyph width: {} (from {} to {})", currentTiming.getGlyphWidth(), currentTiming.glyphStart, currentTiming.glyphEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ public class GeneralSection {
|
||||
*/
|
||||
private String order;
|
||||
|
||||
// @gemini implement
|
||||
private int lineHeight;
|
||||
|
||||
public int getLineHeight() {
|
||||
return lineHeight;
|
||||
}
|
||||
|
||||
public void setLineHeight(int lineHeight) {
|
||||
this.lineHeight = lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Graphics context and rendering utilities.
|
||||
*/
|
||||
@@ -23,15 +25,20 @@ public class GraphicsContext {
|
||||
private final int width;
|
||||
private final BitmapFont font;
|
||||
|
||||
private final java.util.Stack<GraphicsState> stateStack = new java.util.Stack<>();
|
||||
private final Stack<GraphicsState> stateStack = new java.util.Stack<>();
|
||||
|
||||
private int translateX = 0;
|
||||
private int translateY = 0;
|
||||
|
||||
private static class GraphicsState {
|
||||
private final com.badlogic.gdx.math.Matrix4 transform;
|
||||
private final boolean scissorEnabled;
|
||||
private final int translateX;
|
||||
private final int translateY;
|
||||
private int scissorCount;
|
||||
|
||||
public GraphicsState(com.badlogic.gdx.math.Matrix4 transform, boolean scissorEnabled) {
|
||||
this.transform = transform.cpy();
|
||||
this.scissorEnabled = scissorEnabled;
|
||||
public GraphicsState(int translateX, int translateY, int scissorCount) {
|
||||
this.translateX = translateX;
|
||||
this.translateY = translateY;
|
||||
this.scissorCount = scissorCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +51,7 @@ public class GraphicsContext {
|
||||
* @param height The height.
|
||||
*/
|
||||
public void draw(NinePatch image, int x, int y, int width, int height) {
|
||||
image.draw(batch, x, getHeight() - height - y, width, height);
|
||||
image.draw(batch, x + translateX, getHeight() - height - (y + translateY), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +61,7 @@ public class GraphicsContext {
|
||||
* @param y The y-coordinate.
|
||||
*/
|
||||
public void draw(Texture texture, int x, int y) {
|
||||
draw(texture, x, y, Parameters.DEFAULT);
|
||||
draw(texture, x + translateX, y + translateY, Parameters.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +73,7 @@ public class GraphicsContext {
|
||||
*/
|
||||
public void draw(Texture texture, int x, int y, Parameters params) {
|
||||
batch.draw(texture,
|
||||
x, getHeight() - y - texture.getHeight(), texture.getWidth(), texture.getHeight(),
|
||||
x + translateX, getHeight() - (y + translateY) - texture.getHeight(), texture.getWidth(), texture.getHeight(),
|
||||
0, 0, texture.getWidth(), texture.getHeight(),
|
||||
params.flipX, params.flipY);
|
||||
}
|
||||
@@ -78,7 +85,7 @@ public class GraphicsContext {
|
||||
* @param y The y coordinate to draw at.
|
||||
*/
|
||||
public void draw(String text, int x, int y) {
|
||||
font.draw(batch, text, x, getHeight() - (y + font.getLineHeight()));
|
||||
font.draw(batch, text, x + translateX, getHeight() - (y + translateY + font.getLineHeight()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +97,7 @@ public class GraphicsContext {
|
||||
*/
|
||||
public void fillRect(int x, int y, int width, int height) {
|
||||
var texture = DiceOS.getResourceLoader().loadTexture("system/white.png");
|
||||
batch.draw(texture, x, getHeight() - height - y, width, height); // This is not ideal.
|
||||
batch.draw(texture, x + translateX, getHeight() - height - (y + translateY), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +110,14 @@ public class GraphicsContext {
|
||||
batch.setColor(r / 255f, g / 255f, b / 255f, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color for subsequent drawing operations using an RGB integer.
|
||||
* @param rgb The RGB color integer.
|
||||
*/
|
||||
public void setColour(int rgb) {
|
||||
setColour((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts the context to the right and down by some amount.
|
||||
* Negative values move towards the opposite direction.
|
||||
@@ -111,7 +126,21 @@ public class GraphicsContext {
|
||||
* @param y The number of pixels to move down by.
|
||||
*/
|
||||
public void translate(int x, int y) {
|
||||
batch.setTransformMatrix(batch.getTransformMatrix().trn(x, -y, 0));
|
||||
translateX += x;
|
||||
translateY += y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total amount of X-translation applied by this graphics context.
|
||||
* In other words, a draw command with `x=0` would actually write to this screen coordinate.
|
||||
* @return The total amount of X-translation.
|
||||
*/
|
||||
public int getXTranslation() {
|
||||
return translateX;
|
||||
}
|
||||
|
||||
public int getYTranslation() {
|
||||
return translateY;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,10 +150,32 @@ public class GraphicsContext {
|
||||
* @param height The height of the clipping area.
|
||||
*/
|
||||
public void clip(int width, int height) {
|
||||
var scissors = new Rectangle(0, 0, width, height);
|
||||
clip(0, 0, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a clipping rectangle for subsequent draw operations.
|
||||
*
|
||||
* @param x The x-coordinate of the clipping area.
|
||||
* @param y The y-coordinate of the clipping area.
|
||||
* @param width The width of the clipping area.
|
||||
* @param height The height of the clipping area.
|
||||
*/
|
||||
public void clip(int x, int y, int width, int height) {
|
||||
var scissors = new Rectangle(x + getXTranslation(), getHeight() - (y + getYTranslation()) - height, width, height);
|
||||
ScissorStack.calculateScissors(camera, batch.getTransformMatrix(), scissors, scissors);
|
||||
batch.flush();
|
||||
ScissorStack.pushScissors(scissors);
|
||||
if (ScissorStack.pushScissors(scissors)) {
|
||||
getCurrentState().scissorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current graphics state from the top of the stack.
|
||||
* @return The current state.
|
||||
*/
|
||||
private GraphicsState getCurrentState() {
|
||||
return stateStack.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +183,7 @@ public class GraphicsContext {
|
||||
* The state can later be restored using {@link #restore()}.
|
||||
*/
|
||||
public void save() {
|
||||
stateStack.push(new GraphicsState(batch.getTransformMatrix(), false));
|
||||
stateStack.push(new GraphicsState(translateX, translateY, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +196,11 @@ public class GraphicsContext {
|
||||
}
|
||||
batch.flush();
|
||||
GraphicsState state = stateStack.pop();
|
||||
batch.setTransformMatrix(state.transform);
|
||||
translateX = state.translateX;
|
||||
translateY = state.translateY;
|
||||
for (int i = 0; i < state.scissorCount; i++) {
|
||||
ScissorStack.popScissors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,9 +17,17 @@ public class MouseClickEvent extends MouseEvent {
|
||||
*/
|
||||
private final Button button;
|
||||
|
||||
/**
|
||||
* The action performed in the mouse click event, indicating whether the mouse button
|
||||
* was pressed or released.
|
||||
*/
|
||||
private final Action action;
|
||||
|
||||
public enum Button {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
;
|
||||
LEFT, RIGHT,
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
PRESSED, RELEASED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package be.seeseemelk.diceos.system.toolkit.menu;
|
||||
|
||||
import be.seeseemelk.diceos.system.DiceOS;
|
||||
import be.seeseemelk.diceos.system.font.Font;
|
||||
import be.seeseemelk.diceos.system.gfx.GraphicsContext;
|
||||
import be.seeseemelk.diceos.system.toolkit.Window;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class Menu implements MenuItem {
|
||||
private final static int HORIZONTAL_PADDING = 2;
|
||||
|
||||
private final String title;
|
||||
private final List<MenuItem> items = new ArrayList<>();
|
||||
private final Font font = DiceOS.getResourceLoader().getDefaultFont();
|
||||
|
||||
public Menu add(MenuItem item) {
|
||||
items.add(item);
|
||||
@@ -26,12 +33,12 @@ public class Menu implements MenuItem {
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return items.stream().mapToInt(MenuItem::getWidth).max().orElse(0);
|
||||
return font.getTextWidth(title) + HORIZONTAL_PADDING * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return items.stream().mapToInt(MenuItem::getHeight).sum();
|
||||
return 13;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,15 +46,23 @@ public class Menu implements MenuItem {
|
||||
*
|
||||
* @param x The x-coordinate.
|
||||
* @param y The y-coordinate.
|
||||
* @return The created MenuPopup.
|
||||
* @return The created Window for the menu.
|
||||
*/
|
||||
public MenuPopup show(int x, int y) {
|
||||
return new MenuPopup(this, x, y);
|
||||
public Window show(int x, int y) {
|
||||
return new Window(x, y, getWidth(), getHeight(), Window.Style.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(GraphicsContext gc) {
|
||||
if (DiceOS.getInputService().mouseInBounds(gc.getXTranslation(), gc.getYTranslation(), getWidth(), getHeight())) {
|
||||
log.info("Mouse in bounds");
|
||||
gc.setColour(0x9f7445);
|
||||
gc.fillRect(-100, -100, 500, 500);
|
||||
}
|
||||
|
||||
gc.setColour(255, 255, 255);
|
||||
|
||||
// Renders the menu as a simple menu button
|
||||
gc.draw(title, 0, 5);
|
||||
gc.draw(title, HORIZONTAL_PADDING, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package be.seeseemelk.diceos.system.toolkit.menu;
|
||||
|
||||
import be.seeseemelk.diceos.system.toolkit.Window;
|
||||
|
||||
/**
|
||||
* A visisble menu popup.
|
||||
*/
|
||||
public class MenuPopup {
|
||||
private final Menu menu;
|
||||
private final Window window;
|
||||
|
||||
public MenuPopup(Menu menu, int x, int y) {
|
||||
this.menu = menu;
|
||||
this.window = new Window(x, y, menu.getWidth(), menu.getHeight(), Window.Style.NONE);
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,18 @@ public class Utils {
|
||||
public static boolean isPowerOfTwo(int value) {
|
||||
return (value & (value - 1)) == 0 && value != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the point at (x, y) is inside the bounds of (x1, y1, width, height).
|
||||
* @param x The x-coordinate of the point.
|
||||
* @param y The y-coordinate of the point.
|
||||
* @param x1 The x-coordinate of the bounds.
|
||||
* @param y1 The y-coordinate of the bounds.
|
||||
* @param width The width of the bounds.
|
||||
* @param height The height of the bounds.
|
||||
* @return True if the point is inside the bounds, false otherwise.
|
||||
*/
|
||||
public static boolean isInBounds(int x, int y, int x1, int y1, int width, int height) {
|
||||
return x >= x1 && x < x1 + width && y >= y1 && y < y1 + height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package be.seeseemelk.diceos.system;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class InputServiceTest {
|
||||
private DisplayService displayService;
|
||||
private InputService inputService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
displayService = Mockito.mock(DisplayService.class);
|
||||
inputService = new InputService(displayService);
|
||||
Gdx.input = Mockito.mock(Input.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMouseMethods() {
|
||||
Mockito.when(Gdx.input.getX()).thenReturn(100);
|
||||
Mockito.when(Gdx.input.getY()).thenReturn(200);
|
||||
|
||||
assertEquals(100, inputService.getMouseX());
|
||||
assertEquals(200, inputService.getMouseY());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMouseInBounds() {
|
||||
Mockito.when(Gdx.input.getX()).thenReturn(100);
|
||||
Mockito.when(Gdx.input.getY()).thenReturn(200);
|
||||
|
||||
assertTrue(inputService.mouseInBounds(90, 190, 20, 20));
|
||||
assertFalse(inputService.mouseInBounds(0, 0, 50, 50));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package be.seeseemelk.diceos.system.gfx;
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.Batch;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.NinePatch;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class GraphicsContextTest {
|
||||
private Camera camera;
|
||||
private Batch batch;
|
||||
private BitmapFont font;
|
||||
private GraphicsContext gc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
camera = mock(Camera.class);
|
||||
batch = mock(Batch.class);
|
||||
font = mock(BitmapFont.class);
|
||||
gc = new GraphicsContext(camera, batch, 600, 800, font);
|
||||
when(batch.getTransformMatrix()).thenReturn(new Matrix4());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDrawNinePatch() {
|
||||
NinePatch np = mock(NinePatch.class);
|
||||
gc.draw(np, 10, 20, 100, 200);
|
||||
verify(np).draw(eq(batch), eq(10f), eq(380f), eq(100f), eq(200f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDrawTexture() {
|
||||
Texture tex = mock(Texture.class);
|
||||
when(tex.getHeight()).thenReturn(50);
|
||||
when(tex.getWidth()).thenReturn(50);
|
||||
gc.draw(tex, 10, 20);
|
||||
verify(batch).draw(eq(tex), eq(10f), anyFloat(), anyFloat(), anyFloat(), anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDrawText() {
|
||||
gc.draw("test", 10, 20);
|
||||
verify(font).draw(batch, "test", 10f, 600f - (20f + font.getLineHeight()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetColour() {
|
||||
gc.setColour(255, 0, 0);
|
||||
verify(batch).setColor(1f, 0f, 0f, 1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetColourRGB() {
|
||||
gc.setColour(0xFF00FF);
|
||||
verify(batch).setColor(1f, 0f, 1f, 1f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTranslate() {
|
||||
gc.translate(10, 20);
|
||||
assertEquals(10, gc.getXTranslation());
|
||||
assertEquals(20, gc.getYTranslation());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSaveRestore() {
|
||||
gc.save();
|
||||
assertDoesNotThrow(() -> gc.restore());
|
||||
assertThrows(IllegalStateException.class, () -> gc.restore());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user