mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
Removed bad idea from gdag and added text alignment.
This commit is contained in:
parent
21a50491b8
commit
d47e02b1d3
4 changed files with 68 additions and 203 deletions
|
@ -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);
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue