diff --git a/GEMINI.md b/GEMINI.md index ad59810..61f43bf 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -15,7 +15,7 @@ The project uses Gradle. * **Language:** Java * **Dependency Injection:** Uses `avaje-inject`. Components are marked with `@Component` and injected via constructor. * **Boilerplate:** Uses `lombok` for boilerplate reduction. -* **Rendering:** Built on LibGDX. Main entry point is `DiceOS.java` which extends `ApplicationAdapter`. +* **Rendering:** Built on LibGDX. Main entry point is `DiceOSAdapter.java` which extends `ApplicationAdapter`. * **Asset Loading:** All assets, including textures, fonts, and NinePatches, must be loaded via the `ResourceLoader` service to ensure proper lifecycle management and caching. * **Project Structure:** * `src/main/java/be/seeseemelk/diceos/system`: Core OS services (Display, Window, Input, etc.). diff --git a/src/main/java/be/seeseemelk/diceos/Bootloader.java b/src/main/java/be/seeseemelk/diceos/Bootloader.java index 332cafc..d1f2768 100644 --- a/src/main/java/be/seeseemelk/diceos/Bootloader.java +++ b/src/main/java/be/seeseemelk/diceos/Bootloader.java @@ -1,6 +1,7 @@ package be.seeseemelk.diceos; import be.seeseemelk.diceos.system.DiceOS; +import be.seeseemelk.diceos.system.DiceOSAdapter; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.graphics.glutils.HdpiMode; @@ -13,7 +14,8 @@ public class Bootloader { config.setTitle("DiceOS"); config.setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode()); config.setHdpiMode(HdpiMode.Pixels); - new Lwjgl3Application(beanScope.get(DiceOS.class), config); + beanScope.get(DiceOS.class); + new Lwjgl3Application(beanScope.get(DiceOSAdapter.class), config); } } } diff --git a/src/main/java/be/seeseemelk/diceos/system/DiceOS.java b/src/main/java/be/seeseemelk/diceos/system/DiceOS.java index d68ebe2..ad21173 100644 --- a/src/main/java/be/seeseemelk/diceos/system/DiceOS.java +++ b/src/main/java/be/seeseemelk/diceos/system/DiceOS.java @@ -1,121 +1,36 @@ package be.seeseemelk.diceos.system; -import be.seeseemelk.diceos.system.gfx.GraphicsContext; -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; -import com.badlogic.gdx.utils.viewport.Viewport; import io.avaje.inject.Component; +import io.avaje.inject.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import java.util.List; - -@Slf4j -@Component @RequiredArgsConstructor -public class DiceOS extends ApplicationAdapter { +@Component +public class DiceOS { + private static DiceOS INSTANCE; private final ResourceLoader resourceLoader; - private final DisplayService display; - private final CursorService cursorService; private final WindowService windowService; - private final List startupTasks; - private Texture clouds; - private Texture border; - private Viewport vmViewport; - private Viewport screenViewport; - private int scaling = 1; - private int offsetX = 0; - private int offsetY = 0; - private GraphicsContext gc; - private BitmapFont font; - - @Override - public void create() { - log.info("DiceOS starting..."); - clouds = resourceLoader.loadTexture("backgrounds/clouds.png"); - border = resourceLoader.loadTexture("system/border.png"); - - screenViewport = new ScreenViewport(); - vmViewport = new ScreenViewport(); - vmViewport.update(display.getWidth(), display.getHeight(), true); - - for (var task : startupTasks) { - task.onStartup(); + @PostConstruct + void register() { + if (INSTANCE != null) { + throw new IllegalStateException("DiceOS already created"); } - - font = resourceLoader.loadFont("system/font"); - - log.info("DiceOS started!"); + INSTANCE = this; } - @Override - public void resize(int width, int height) { - screenViewport.update(width, height, true); - - int displayWidth = display.getWidth(); - int displayHeight = display.getHeight(); - scaling = Math.min(width / displayWidth, height / displayHeight); - offsetX = (width - (displayWidth * scaling)) / 2; - offsetY = (height - (displayHeight * scaling)) / 2; - - cursorService.setScale(scaling); - - gc = new GraphicsContext(screenViewport.getCamera(), display.getBatch(), displayHeight, displayWidth); + public static DiceOS get() { + if (INSTANCE == null) { + throw new IllegalStateException("ResourceLoader not initialized yet"); + } + return INSTANCE; } - @Override - public void render() { - int mouseX = Gdx.input.getX(); - int mouseY = display.getHeight() - Gdx.input.getY(); - windowService.update(mouseX, mouseY, Gdx.input.isTouched()); + public static ResourceLoader getResourceLoader() { + return INSTANCE.resourceLoader; + } - cursorService.updateCursor(); - - ScreenUtils.clear(Color.BLACK); - vmViewport.apply(); - display.getScreenBuffer().begin(); - display.getBatch().setProjectionMatrix(vmViewport.getCamera().combined); - display.getBatch().begin(); - - ScreenUtils.clear(Color.GREEN); - - // Render background - gc.draw(clouds, 0, 0); - - // Render windows - windowService.paint(gc); - - // Render borders - var param = new GraphicsContext.Parameters(); - gc.draw(border, 0, 0, param); - param.flipX = true; - gc.draw(border, display.getWidth() - 8, 0, param); - param.flipY = true; - gc.draw(border, display.getWidth() - 8, display.getHeight() - 8, param); - param.flipX = false; - gc.draw(border, 0, display.getHeight() - 8, param); - - font.draw(display.getBatch(), "The quick brown fox jumps over the lazy dog", 6, display.getHeight() - 12); - - // Finish rendering to screen buffer - display.getBatch().end(); - display.getScreenBuffer().end(); - - // Render screen buffer to actual screen - screenViewport.apply(); - display.getBatch().setProjectionMatrix(screenViewport.getCamera().combined); - display.getBatch().begin(); - var texture = display.getScreenBuffer().getColorBufferTexture(); - var textureRegion = new TextureRegion(texture); - textureRegion.flip(false, true); - display.getBatch().draw(textureRegion, offsetX, offsetY, display.getWidth() * scaling, display.getHeight() * scaling); - display.getBatch().end(); + public static WindowService getWindowService() { + return INSTANCE.windowService; } } diff --git a/src/main/java/be/seeseemelk/diceos/system/DiceOSAdapter.java b/src/main/java/be/seeseemelk/diceos/system/DiceOSAdapter.java new file mode 100644 index 0000000..7e7e771 --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/DiceOSAdapter.java @@ -0,0 +1,121 @@ +package be.seeseemelk.diceos.system; + +import be.seeseemelk.diceos.system.gfx.GraphicsContext; +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; +import com.badlogic.gdx.utils.viewport.Viewport; +import io.avaje.inject.Component; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DiceOSAdapter extends ApplicationAdapter { + private final ResourceLoader resourceLoader; + private final DisplayService display; + private final CursorService cursorService; + private final WindowService windowService; + private final List startupTasks; + private Texture clouds; + private Texture border; + private Viewport vmViewport; + private Viewport screenViewport; + private int scaling = 1; + private int offsetX = 0; + private int offsetY = 0; + private GraphicsContext gc; + + private BitmapFont font; + + @Override + public void create() { + log.info("DiceOS starting..."); + clouds = resourceLoader.loadTexture("backgrounds/clouds.png"); + border = resourceLoader.loadTexture("system/border.png"); + + screenViewport = new ScreenViewport(); + vmViewport = new ScreenViewport(); + vmViewport.update(display.getWidth(), display.getHeight(), true); + + for (var task : startupTasks) { + task.onStartup(); + } + + font = resourceLoader.loadFont("system/font"); + + log.info("DiceOS started!"); + } + + @Override + public void resize(int width, int height) { + screenViewport.update(width, height, true); + + int displayWidth = display.getWidth(); + int displayHeight = display.getHeight(); + scaling = Math.min(width / displayWidth, height / displayHeight); + offsetX = (width - (displayWidth * scaling)) / 2; + offsetY = (height - (displayHeight * scaling)) / 2; + + cursorService.setScale(scaling); + + gc = new GraphicsContext(screenViewport.getCamera(), display.getBatch(), displayHeight, displayWidth, font); + } + + @Override + public void render() { + int mouseX = Gdx.input.getX(); + int mouseY = display.getHeight() - Gdx.input.getY(); + windowService.update(mouseX, mouseY, Gdx.input.isTouched()); + + cursorService.updateCursor(); + + ScreenUtils.clear(Color.BLACK); + vmViewport.apply(); + display.getScreenBuffer().begin(); + display.getBatch().setProjectionMatrix(vmViewport.getCamera().combined); + display.getBatch().begin(); + + ScreenUtils.clear(Color.GREEN); + + // Render background + gc.draw(clouds, 0, 0); + + // Render windows + windowService.paint(gc); + + // Render borders + var param = new GraphicsContext.Parameters(); + gc.draw(border, 0, 0, param); + param.flipX = true; + gc.draw(border, display.getWidth() - 8, 0, param); + param.flipY = true; + gc.draw(border, display.getWidth() - 8, display.getHeight() - 8, param); + param.flipX = false; + gc.draw(border, 0, display.getHeight() - 8, param); + + // Finish rendering to screen buffer + display.getBatch().end(); + display.getScreenBuffer().end(); + + // Render screen buffer to actual screen + screenViewport.apply(); + display.getBatch().setProjectionMatrix(screenViewport.getCamera().combined); + display.getBatch().begin(); + var texture = display.getScreenBuffer().getColorBufferTexture(); + var textureRegion = new TextureRegion(texture); + textureRegion.flip(false, true); + display.getBatch().draw(textureRegion, offsetX, offsetY, display.getWidth() * scaling, display.getHeight() * scaling); + display.getBatch().end(); + } + + +} diff --git a/src/main/java/be/seeseemelk/diceos/system/WindowService.java b/src/main/java/be/seeseemelk/diceos/system/WindowService.java index 4bef49b..80bce39 100644 --- a/src/main/java/be/seeseemelk/diceos/system/WindowService.java +++ b/src/main/java/be/seeseemelk/diceos/system/WindowService.java @@ -16,13 +16,11 @@ public class WindowService implements OnStartup { private final ResourceLoader resourceLoader; private final List windows = new ArrayList<>(); private NinePatch menubar; - private NinePatch windowDecoration; private Menu menu; @Override public void onStartup() { menubar = resourceLoader.loadNinePatch("system/menubar.png"); - windowDecoration = resourceLoader.loadNinePatch("system/window.png"); menu = new Menu("Root"); menu.add(new Menu("Dice")); @@ -30,7 +28,25 @@ public class WindowService implements OnStartup { } public void spawnWindow(int x, int y, int width, int height) { - windows.add(new Window(x, y, width, height)); + windows.add(new Window(x, y, width, height, Window.Style.NORMAL)); + } + + /** + * Maps a window to be managed by this service. + * @param window The window to manage. + */ + public void map(Window window) { + if (!windows.contains(window)) { + windows.add(window); + } + } + + /** + * Unmaps a window, removing it from management. + * @param window The window to remove. + */ + public void unmap(Window window) { + windows.remove(window); } public void update(int mouseX, int mouseY, boolean isTouched) { @@ -48,8 +64,12 @@ public class WindowService implements OnStartup { gc.draw(menubar, 0, 0, gc.getWidth(), 14); // Render menubar items - if (menu != null) { - menu.paint(gc); + gc.save(); + gc.translate(5, 0); + for (var submenu : menu.getItems()) { + submenu.paint(gc); + gc.translate(submenu.getWidth(), 0); } + gc.restore(); } } diff --git a/src/main/java/be/seeseemelk/diceos/system/font/FontLoader.java b/src/main/java/be/seeseemelk/diceos/system/font/FontLoader.java index 4ab7f5b..fdafb31 100644 --- a/src/main/java/be/seeseemelk/diceos/system/font/FontLoader.java +++ b/src/main/java/be/seeseemelk/diceos/system/font/FontLoader.java @@ -27,6 +27,7 @@ public class FontLoader { var region = new TextureRegion(texture); var data = new BitmapFont.BitmapFontData(); + data.lineHeight = metadata.getGeneral().getLineHeight(); var glyphMap = new Map2D(); var glyphLines = metadata.getGeneral().getOrder().split("\n"); @@ -65,6 +66,8 @@ public class FontLoader { glyph.u2 = (float)widthTiming.glyphEnd / pixmap.getWidth(); glyph.v = (float)heightTiming.glyphStart / pixmap.getHeight(); glyph.v2 = (float)heightTiming.glyphEnd / pixmap.getHeight(); + + data.capHeight = Math.max(data.capHeight, heightTiming.getGlyphHeight()); } } diff --git a/src/main/java/be/seeseemelk/diceos/system/font/GeneralSection.java b/src/main/java/be/seeseemelk/diceos/system/font/GeneralSection.java index c37c9df..d046c95 100644 --- a/src/main/java/be/seeseemelk/diceos/system/font/GeneralSection.java +++ b/src/main/java/be/seeseemelk/diceos/system/font/GeneralSection.java @@ -15,4 +15,7 @@ public class GeneralSection { * The order of the glyphs in the image file. */ private String order; + + // @gemini implement + private int lineHeight; } diff --git a/src/main/java/be/seeseemelk/diceos/system/gfx/GraphicsContext.java b/src/main/java/be/seeseemelk/diceos/system/gfx/GraphicsContext.java index 8f9ffa9..ff523fc 100644 --- a/src/main/java/be/seeseemelk/diceos/system/gfx/GraphicsContext.java +++ b/src/main/java/be/seeseemelk/diceos/system/gfx/GraphicsContext.java @@ -1,16 +1,16 @@ package be.seeseemelk.diceos.system.gfx; +import be.seeseemelk.diceos.system.DiceOS; 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.Rectangle; import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.function.Consumer; - /** * Graphics context and rendering utilities. */ @@ -21,9 +21,19 @@ public class GraphicsContext { private final Batch batch; private final int height; private final int width; + private final BitmapFont font; - private final com.badlogic.gdx.math.Matrix4 originalTransform = new com.badlogic.gdx.math.Matrix4(); - private final java.util.Stack transformStack = new java.util.Stack<>(); + private final java.util.Stack stateStack = new java.util.Stack<>(); + + private static class GraphicsState { + private final com.badlogic.gdx.math.Matrix4 transform; + private final boolean scissorEnabled; + + public GraphicsState(com.badlogic.gdx.math.Matrix4 transform, boolean scissorEnabled) { + this.transform = transform.cpy(); + this.scissorEnabled = scissorEnabled; + } + } /** * Draws a NinePatch image at the specified coordinates and dimensions. @@ -61,6 +71,16 @@ public class GraphicsContext { params.flipX, params.flipY); } + /** + * Draws some text with the current font. + * @param text The text to draw. + * @param x The x coordinate to draw at. + * @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())); + } + /** * Fills a rectangular area with the currently set color. * @param x The x-coordinate. @@ -69,7 +89,8 @@ public class GraphicsContext { * @param height The height. */ public void fillRect(int x, int y, int width, int height) { - batch.draw(com.badlogic.gdx.graphics.Texture.class.cast(null), x, getHeight() - height - y, width, height); // This is not ideal. + var texture = DiceOS.getResourceLoader().loadTexture("system/white.png"); + batch.draw(texture, x, getHeight() - height - y, width, height); // This is not ideal. } /** @@ -84,13 +105,13 @@ public class GraphicsContext { /** * Shifts the context to the right and down by some amount. - * Negative values move to the opposite direction. + * Negative values move towards the opposite direction. * * @param x The number of pixels to move to the right by. * @param y The number of pixels to move down by. */ public void translate(int x, int y) { - batch.getTransformMatrix().trn(x, -y, 0); + batch.setTransformMatrix(batch.getTransformMatrix().trn(x, -y, 0)); } /** @@ -111,7 +132,7 @@ public class GraphicsContext { * The state can later be restored using {@link #restore()}. */ public void save() { - transformStack.push(batch.getTransformMatrix().cpy()); + stateStack.push(new GraphicsState(batch.getTransformMatrix(), false)); } /** @@ -119,12 +140,12 @@ public class GraphicsContext { * @throws IllegalStateException if no state was pushed. */ public void restore() { - if (transformStack.isEmpty()) { + if (stateStack.isEmpty()) { throw new IllegalStateException("No state to restore"); } batch.flush(); - ScissorStack.popScissors(); - batch.setTransformMatrix(transformStack.pop()); + GraphicsState state = stateStack.pop(); + batch.setTransformMatrix(state.transform); } /** diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/Component.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/Component.java index f8c2b3c..4afaf2e 100644 --- a/src/main/java/be/seeseemelk/diceos/system/toolkit/Component.java +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/Component.java @@ -1,7 +1,40 @@ package be.seeseemelk.diceos.system.toolkit; import be.seeseemelk.diceos.system.gfx.GraphicsContext; +import be.seeseemelk.diceos.system.toolkit.events.Event; +import be.seeseemelk.diceos.system.toolkit.events.InputEvent; +import be.seeseemelk.diceos.system.toolkit.events.MouseEvent; public abstract class Component { + public abstract int getX(); + public abstract int getY(); + public abstract int getWidth(); + public abstract int getHeight(); + public abstract void paint(GraphicsContext gc); + + public void onEvent(Event event) { + switch (event) { + case InputEvent inputEvent -> onInputEvent(inputEvent); + default -> {} + } + } + + public void onInputEvent(InputEvent event) { + switch (event) { + case MouseEvent mouseEvent -> onMouseEvent(mouseEvent); + default -> {} + } + } + + public void onMouseEvent(MouseEvent event) { + switch (event) { + case be.seeseemelk.diceos.system.toolkit.events.MouseClickEvent mouseClickEvent -> onMouseClick(mouseClickEvent); + default -> {} + } + } + + public void onMouseClick(be.seeseemelk.diceos.system.toolkit.events.MouseClickEvent event) { + // Default empty implementation + } } diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/Container.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/Container.java index 82e014f..095e776 100644 --- a/src/main/java/be/seeseemelk/diceos/system/toolkit/Container.java +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/Container.java @@ -1,6 +1,7 @@ package be.seeseemelk.diceos.system.toolkit; import be.seeseemelk.diceos.system.gfx.GraphicsContext; +import be.seeseemelk.diceos.system.toolkit.events.MouseEvent; import java.util.ArrayList; import java.util.List; @@ -22,4 +23,14 @@ public abstract class Container extends Component { component.paint(gc); } } + + @Override + public void onMouseEvent(MouseEvent event) { + super.onMouseEvent(event); + for (var component : components) { + if (component instanceof Window window && window.isHovered(event.getX(), event.getY())) { + component.onMouseEvent(event); + } + } + } } diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/Window.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/Window.java index 158ed96..c19e266 100644 --- a/src/main/java/be/seeseemelk/diceos/system/toolkit/Window.java +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/Window.java @@ -1,5 +1,6 @@ package be.seeseemelk.diceos.system.toolkit; +import be.seeseemelk.diceos.system.DiceOS; import be.seeseemelk.diceos.system.gfx.GraphicsContext; import com.badlogic.gdx.graphics.g2d.NinePatch; import lombok.Getter; @@ -14,7 +15,7 @@ public class Window extends Container { private String title; private int x, y, width, height; private boolean active; - private Style style; + private NinePatch borders; /** * The style of window decorations to use. @@ -32,12 +33,33 @@ public class Window extends Container { NONE, } - public Window(int x, int y, int width, int height) { + public Window(int x, int y, int width, int height, Style style) { this.x = x; this.y = y; this.width = width; this.height = height; - this.style = Style.NORMAL; + setStyle(style); + } + + public void setStyle(Style style) { + switch (style) { + case NORMAL -> borders = DiceOS.getResourceLoader().loadNinePatch("system/window.png"); + case NONE -> borders = null; + } + } + + /** + * Shows this window in the system. + */ + public void show() { + DiceOS.getWindowService().map(this); + } + + /** + * Hides this window from the system. + */ + public void hide() { + DiceOS.getWindowService().unmap(this); } public boolean isHovered(int mouseX, int mouseY) { @@ -66,6 +88,9 @@ public class Window extends Container { @Override public void paint(GraphicsContext gc) { + if (borders != null) { + borders.draw(gc.getBatch(), x, gc.getHeight() - height - y, width, height); + } super.paint(gc); } } diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/events/Event.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/Event.java new file mode 100644 index 0000000..1829fb1 --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/Event.java @@ -0,0 +1,4 @@ +package be.seeseemelk.diceos.system.toolkit.events; + +public abstract class Event { +} diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/events/InputEvent.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/InputEvent.java new file mode 100644 index 0000000..2f9e71c --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/InputEvent.java @@ -0,0 +1,4 @@ +package be.seeseemelk.diceos.system.toolkit.events; + +public abstract class InputEvent extends Event { +} diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseClickEvent.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseClickEvent.java new file mode 100644 index 0000000..9afbf0e --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseClickEvent.java @@ -0,0 +1,25 @@ +package be.seeseemelk.diceos.system.toolkit.events; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Event representing a mouse button click. + */ +@Getter +@RequiredArgsConstructor +public class MouseClickEvent extends MouseEvent { + private final int x; + private final int y; + + /** + * The mouse button that was clicked. + */ + private final Button button; + + public enum Button { + LEFT, + RIGHT, + ; + } +} diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseEvent.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseEvent.java new file mode 100644 index 0000000..db13b25 --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/events/MouseEvent.java @@ -0,0 +1,16 @@ +package be.seeseemelk.diceos.system.toolkit.events; + +/** + * Base class for all mouse-related input events. + */ +public abstract class MouseEvent extends InputEvent { + /** + * @return The x-coordinate of the mouse event. + */ + public abstract int getX(); + + /** + * @return The y-coordinate of the mouse event. + */ + public abstract int getY(); +} diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/Menu.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/Menu.java index e36f371..e155ec3 100644 --- a/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/Menu.java +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/Menu.java @@ -1,5 +1,6 @@ package be.seeseemelk.diceos.system.toolkit.menu; +import be.seeseemelk.diceos.system.DiceOS; import be.seeseemelk.diceos.system.gfx.GraphicsContext; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -33,17 +34,20 @@ public class Menu implements MenuItem { return items.stream().mapToInt(MenuItem::getHeight).sum(); } + /** + * Opens the popup at a specific coordinate. + * + * @param x The x-coordinate. + * @param y The y-coordinate. + * @return The created MenuPopup. + */ + public MenuPopup show(int x, int y) { + return new MenuPopup(this, x, y); + } + @Override public void paint(GraphicsContext gc) { - // @gemini re-implement - // Need to render background and offset items - // Since GC doesn't have direct rect methods, I'll use a simple approach with the batch - // For now, focusing on correct rendering of items - - int currentY = 0; - for (MenuItem item : items) { - item.paint(gc); - currentY += item.getHeight(); - } + // Renders the menu as a simple menu button + gc.draw(title, 0, 5); } } diff --git a/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/MenuPopup.java b/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/MenuPopup.java new file mode 100644 index 0000000..ab220d8 --- /dev/null +++ b/src/main/java/be/seeseemelk/diceos/system/toolkit/menu/MenuPopup.java @@ -0,0 +1,16 @@ +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); + } +} diff --git a/src/main/resources/system/font.toml b/src/main/resources/system/font.toml index 8ba2b1b..07191dd 100644 --- a/src/main/resources/system/font.toml +++ b/src/main/resources/system/font.toml @@ -4,6 +4,7 @@ order = """ aAbBcCdDeEfFgGhHiIjJkKlLmMnN oOpPqQrRsStTuUvVwWxXyYzZ """ +lineHeight = 7 [padding] horizontal = 1 diff --git a/src/main/resources/system/white.png b/src/main/resources/system/white.png new file mode 100644 index 0000000..89d23b0 Binary files /dev/null and b/src/main/resources/system/white.png differ