Removed bad idea from gdag and added text alignment.

This commit is contained in:
Kapendev 2024-12-17 19:58:16 +02:00
parent 21a50491b8
commit d47e02b1d3
4 changed files with 68 additions and 203 deletions

View file

@ -1,84 +0,0 @@
/// This example shows how to use the scene manager of Parin.
import parin;
import parin.scene;
auto sceneManager = SceneManager();
// The first scene.
struct Scene1 {
mixin extendScene;
float counter = 0;
void ready() {
// The scene manager generates a unique tag for each scene.
printfln("Entering scene 1 with tag {}.", sceneManager.tag);
setBackgroundColor(gray1);
}
bool update(float dt) {
if (Keyboard.space.isPressed) {
sceneManager.enter!Scene2();
}
counter += 5 * dt;
drawDebugText("Press space to change scene.", resolution * Vec2(0.5), DrawOptions(Hook.center));
drawDebugText("Scene 1\nCounter: {}".format(cast(int) counter), Vec2(8));
return false;
}
void finish() {
// The scene tag can be used to free all resources associated with the current scene.
// In this example, no resources will be freed as there are none to free.
printfln("Exiting scene 1 with tag {}.", sceneManager.tag);
freeResources(sceneManager.tag);
}
}
// The second scene.
struct Scene2 {
mixin extendScene;
void ready() {
// The scene manager generates a unique tag for each scene.
printfln("Entering scene 2 with tag {}.", sceneManager.tag);
setBackgroundColor(gray2);
}
bool update(float dt) {
if (Keyboard.space.isPressed) {
sceneManager.enter!Scene1();
}
drawDebugText("Press space to change scene.", resolution * Vec2(0.5), DrawOptions(Hook.center));
drawDebugText("Scene 2\nNo counter here.", Vec2(8));
return false;
}
void finish() {
// The scene tag can be used to free all resources associated with the current scene.
// In this example, no resources will be freed as there are none to free.
printfln("Exiting scene 2 with tag {}.", sceneManager.tag);
freeResources(sceneManager.tag);
}
}
void ready() {
lockResolution(320, 180);
// Enter the first scene. This will call the ready function of that scene.
sceneManager.enter!Scene1();
}
bool update(float dt) {
// Update the current scene.
return sceneManager.update(dt);
}
void finish() {
// Free the scene manager. This will call the finish function of the current scene.
sceneManager.free();
}
mixin runGame!(ready, update, finish);

View file

@ -6,7 +6,6 @@
// Version: v0.0.29
// ---
// TODO: Add way to align text. I neeed this for the UI.
// TODO: Test the resource loading code.
// TODO: Think about the sound API.
// TODO: Make sounds loop based on a variable and not on the file type.
@ -44,7 +43,7 @@ enum Layout : ubyte {
}
/// A type representing alignment orientations.
enum Align : ubyte {
enum Alignment : ubyte {
left, /// Align to the left.
center, /// Align to the center.
right, /// Align to the right.
@ -190,12 +189,13 @@ enum Gamepad : ubyte {
/// A structure containing options for configuring drawing parameters.
struct DrawOptions {
Vec2 origin = Vec2(0.0f); /// The origin point of the drawn object.
Vec2 scale = Vec2(1.0f); /// The scale of the drawn object.
float rotation = 0.0f; /// The rotation of the drawn object, in degrees.
Color color = white; /// The color of the drawn object.
Hook hook = Hook.topLeft; /// A value representing the origin point of the drawn object when origin is set to zero.
Flip flip = Flip.none; /// A value representing flipping orientations.
Vec2 origin = Vec2(0.0f); /// The origin point of the drawn object.
Vec2 scale = Vec2(1.0f); /// The scale of the drawn object.
float rotation = 0.0f; /// The rotation of the drawn object, in degrees.
Color color = white; /// The color of the drawn object.
Hook hook = Hook.topLeft; /// A value representing the origin point of the drawn object when origin is set to zero.
Alignment alignment = Alignment.left; /// A value represeting alignment orientations.
Flip flip = Flip.none; /// A value representing flipping orientations.
@safe @nogc nothrow:
@ -219,6 +219,11 @@ struct DrawOptions {
this.hook = hook;
}
/// Initializes the options with the given alignment.
this(Alignment alignment) {
this.alignment = alignment;
}
/// Initializes the options with the given flip.
this(Flip flip) {
this.flip = flip;
@ -976,13 +981,13 @@ struct EngineState {
@safe @nogc nothrow:
void free() {
debug {
println("Resources that will be freed automatically:");
println(" Text count: ", resources.texts.length != 0 ? resources.texts.length - 1 : 0);
println(" Texture count: ", resources.textures.length != 0 ? resources.textures.length - 1 : 0);
println(" Font count: ", resources.fonts.length != 0 ? resources.fonts.length - 1 : 0);
println(" Sound count: ", resources.sounds.length != 0 ? resources.sounds.length - 1 : 0);
}
// debug {
// println("Resources that will be freed automatically:");
// println(" Text count: ", resources.texts.length != 0 ? resources.texts.length - 1 : 0);
// println(" Texture count: ", resources.textures.length != 0 ? resources.textures.length - 1 : 0);
// println(" Font count: ", resources.fonts.length != 0 ? resources.fonts.length - 1 : 0);
// println(" Sound count: ", resources.sounds.length != 0 ? resources.sounds.length - 1 : 0);
// }
viewport.free();
resources.free();
tempText.free();
@ -1858,6 +1863,8 @@ float deltaWheel() {
Vec2 measureTextSize(Font font, IStr text, DrawOptions options = DrawOptions()) {
if (font.isEmpty || text.length == 0) return Vec2();
// NOTE: No idea what is happening here. Maybe I should try to make it look more like the drawText function.
auto result = Vec2();
auto tempByteCounter = 0; // Used to count longer text line num chars.
auto byteCounter = 0;
@ -2266,9 +2273,29 @@ void drawRune(FontId font, dchar rune, Vec2 position, DrawOptions options = Draw
/// Draws the specified text with the given font at the given position using the provided draw options.
@trusted
void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOptions()) {
static linesBuffer = FixedList!(IStr, 128)();
static linesWidthBuffer = FixedList!(short, 128)();
if (font.isEmpty || text.length == 0) return;
// TODO: Make it work with negative scale values.
auto origin = Rect(measureTextSize(font, text)).origin(options.hook);
// Get some info about the text.
linesBuffer.clear();
linesWidthBuffer.clear();
auto maxLineWidth = 0;
auto lineStartIndex = 0;
foreach (i, c; text) {
if (c == '\n' || i == text.length - 1) {
linesBuffer.append(text[lineStartIndex .. i + (c != '\n')]);
linesWidthBuffer.append(cast(short) measureTextSize(font, linesBuffer[$ - 1]).x);
if (maxLineWidth < linesWidthBuffer[$ - 1]) maxLineWidth = linesWidthBuffer[$ - 1];
lineStartIndex = cast(int) i + 1;
}
}
lineStartIndex = 0;
// Prepare the the text for drawing.
auto textSize = measureTextSize(font, text);
auto origin = Rect(textSize).origin(options.hook);
rl.rlPushMatrix();
if (isPixelSnapped || isPixelPerfect) {
rl.rlTranslatef(floor(position.x), floor(position.y), 0.0f);
@ -2278,31 +2305,35 @@ void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOpt
rl.rlRotatef(options.rotation, 0.0f, 0.0f, 1.0f);
rl.rlScalef(options.scale.x, options.scale.y, 1.0f);
rl.rlTranslatef(floor(-origin.x), floor(-origin.y), 0.0f);
auto textOffsetY = 0.0f; // Offset between lines (on linebreak '\n').
auto textOffsetX = 0.0f; // Offset X to next character to draw.
auto i = 0;
while (i < text.length) {
// Get next codepoint from byte string and glyph index in font.
auto codepointByteCount = 0;
auto codepoint = rl.GetCodepointNext(&text[i], &codepointByteCount);
auto index = rl.GetGlyphIndex(font.data, codepoint);
if (codepoint == '\n') {
textOffsetY += font.lineSpacing;
textOffsetX = 0.0f;
} else {
// Draw the text.
auto textOffsetY = 0; // Offset between lines (on linebreak '\n').
foreach (i, line; linesBuffer) {
auto textOffsetX = 0; // Offset X to next character to draw.
if (options.alignment == Alignment.center) {
textOffsetX = maxLineWidth / 2 - linesWidthBuffer[i] / 2;
} else if (options.alignment == Alignment.right) {
textOffsetX = maxLineWidth - linesWidthBuffer[i];
}
auto codepointIndex = 0;
while (codepointIndex < line.length) {
// Get next codepoint from byte string and glyph index in font.
auto codepointByteCount = 0;
auto codepoint = rl.GetCodepointNext(&line[codepointIndex], &codepointByteCount);
auto glyphIndex = rl.GetGlyphIndex(font.data, codepoint);
if (codepoint != ' ' && codepoint != '\t') {
auto runeOptions = DrawOptions();
runeOptions.color = options.color;
drawRune(font, codepoint, Vec2(textOffsetX, textOffsetY), runeOptions);
rl.DrawTextCodepoint(font.data, codepoint, rl.Vector2(textOffsetX, textOffsetY), font.size, options.color.toRl());
}
if (font.data.glyphs[index].advanceX == 0) {
textOffsetX += font.data.recs[index].width + font.runeSpacing;
if (font.data.glyphs[glyphIndex].advanceX) {
textOffsetX += font.data.glyphs[glyphIndex].advanceX + font.runeSpacing;
} else {
textOffsetX += font.data.glyphs[index].advanceX + font.runeSpacing;
textOffsetX += cast(int) (font.data.recs[glyphIndex].width) + font.runeSpacing;
}
// Move text bytes counter to next codepoint.
codepointIndex += codepointByteCount;
}
// Move text bytes counter to next codepoint.
i += codepointByteCount;
textOffsetY += font.lineSpacing;
}
rl.rlPopMatrix();
}

View file

@ -1,83 +0,0 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin
// Version: v0.0.29
// ---
// NOTE(Kapendev): I am not a fan of this module and I would remove it, but maybe someone is using it.
// TODO: Update all the doc comments here.
/// The `scene` module provides a simple scene manager.
module parin.scene;
import stdc = joka.stdc;
import joka.types;
@safe:
struct Scene {
void delegate() @trusted ready;
bool delegate(float dt) @trusted update;
void delegate() @trusted finish;
}
struct SceneManager {
Scene* scene;
Scene* nextScene;
Sz tag;
@trusted:
void enter(T)() {
if (nextScene) return;
auto temp = cast(T*) stdc.malloc(T.sizeof);
*temp = T();
temp.prepare();
nextScene = cast(Scene*) temp;
}
bool update(float dt) {
if (nextScene) {
if (scene) {
if (scene.finish) scene.finish();
stdc.free(scene);
}
scene = nextScene;
nextScene = null;
tag = (tag + 1) % Sz.max;
if (tag == 0) tag = 1;
if (scene.ready) scene.ready();
if (scene.update) return scene.update(dt);
return true;
}
if (scene && scene.update) return scene.update(dt);
return true;
}
void free() {
if (scene) {
if (scene.finish) scene.finish();
stdc.free(scene);
scene = null;
}
if (nextScene) {
stdc.free(nextScene);
nextScene = null;
}
tag = 0;
}
}
mixin template extendScene() {
Scene base;
@trusted
void prepare() {
auto base = mixin("&", this.tupleof[0].stringof);
base.ready = cast(void delegate() @trusted) &this.ready;
base.update = cast(bool delegate(float dt) @trusted) &this.update;
base.finish = cast(void delegate() @trusted) &this.finish;
}
}

View file

@ -7,6 +7,7 @@
// ---
// TODO: Clean maybe the UiState struct and prepareUi func.
// TODO: Think about overlapping UI items.
// TODO: Add way to get item point for some stuff. This is nice when making lists.
// TODO: Add focus style.
// TODO: Add way to align text in buttons.
@ -67,7 +68,6 @@ struct UiState {
Gamepad gamepadClickAction = Gamepad.a;
bool isActOnPress;
Vec2 mousePressedPoint;
Vec2 viewportPoint;
Vec2 viewportSize;
Vec2 viewportScale = Vec2(1);
@ -78,6 +78,7 @@ struct UiState {
Vec2 layoutStartPointOffest;
Vec2 layoutMaxItemSize;
Vec2 mousePressedPoint;
Vec2 itemDragOffset;
Vec2 itemPoint;
Vec2 itemSize;