summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-27 23:50:08 +0900
committerAdam Malczewski <[email protected]>2026-06-27 23:50:08 +0900
commit7f0444bc308fef0b78b2fd4fa8aa269da619324c (patch)
tree7b8072098df545192788cee0c44535e9c4f6e95c
parent6bf8907cd69383e8bb0a77b741d314bddf8008e6 (diff)
downloadstudy-player-7f0444bc308fef0b78b2fd4fa8aa269da619324c.tar.gz
study-player-7f0444bc308fef0b78b2fd4fa8aa269da619324c.zip
fix: extract help text + checkbox into ui_render_overlay
The help text and study-mode checkbox were inside ui_render_player, so they only appeared when an audio file was loaded. In the original single-file main.c they rendered unconditionally (outside the 'if loaded' block). Extracted ui_render_overlay() — renders help text + checkbox — and call it from main.c after either ui_render_player or ui_render_empty, matching the original behavior. Also added a screenshot mode (env STUDY_PLAYER_SCREENSHOT) for automated visual testing: renders to an offscreen RenderTexture, fixes FBO alpha artifacts (ClearBackground doesn't fill alpha on some Mesa drivers), and exports a PNG. Verified all UI elements render correctly.
-rw-r--r--src/main.c59
-rw-r--r--src/ui.c55
-rw-r--r--src/ui.h5
3 files changed, 90 insertions, 29 deletions
diff --git a/src/main.c b/src/main.c
index 91dc984..dd90946 100644
--- a/src/main.c
+++ b/src/main.c
@@ -10,6 +10,7 @@
#include "raygui.h"
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "types.h"
@@ -37,6 +38,11 @@ static char exeDir[512];
static int activeTab = 0;
static int prevTab = 0;
+/* --- Screenshot mode (env: STUDY_PLAYER_SCREENSHOT=filename) --- */
+static const char *screenshotFile = NULL;
+static int screenshotFrameCount = 0;
+#define SCREENSHOT_DELAY_FRAMES 60 /* ~1 second at 60fps for window to fully map */
+
/* ------------------------------------------------------------------ */
/* File loading (drag-drop desktop / JS callback web) */
/* ------------------------------------------------------------------ */
@@ -108,11 +114,54 @@ static void update_frame(void)
ui_render_player(&ui, &state, &layout);
else
ui_render_empty(&ui, &layout);
+ ui_render_overlay(&ui, &state, &layout);
} else {
layout_editor_draw(exeDir, &layout);
}
EndDrawing();
+
+ /* --- Screenshot mode: render to FBO, save as PNG, then exit --- */
+ if (screenshotFile) {
+ screenshotFrameCount++;
+ if (screenshotFrameCount >= SCREENSHOT_DELAY_FRAMES) {
+ RenderTexture2D target = LoadRenderTexture(SCREEN_W, SCREEN_H);
+ BeginTextureMode(target);
+ ClearBackground(ui.bgColor);
+ /* Explicitly fill the entire texture with bg color + alpha 255 */
+ DrawRectangle(0, 0, SCREEN_W, SCREEN_H, ui.bgColor);
+ char *tabNames2[] = { "Player", "Layout" };
+ GuiTabBar((Rectangle){ 0, 10, SCREEN_W, 32 }, tabNames2, 2, &activeTab);
+ if (state.loaded)
+ ui_render_player(&ui, &state, &layout);
+ else
+ ui_render_empty(&ui, &layout);
+ ui_render_overlay(&ui, &state, &layout);
+ EndTextureMode();
+
+ /* Read from the render texture — it has proper content */
+ Image img = LoadImageFromTexture(target.texture);
+ ImageFlipVertical(&img);
+ /* ClearBackground doesn't fill FBO alpha on some Mesa drivers.
+ * Manually replace all transparent (alpha=0) pixels with the
+ * background color, so the saved PNG has a proper background. */
+ if (img.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) {
+ unsigned char *px = (unsigned char *)img.data;
+ for (int i = 0; i < img.width * img.height * 4; i += 4) {
+ if (px[i+3] == 0) {
+ px[i] = ui.bgColor.r;
+ px[i+1] = ui.bgColor.g;
+ px[i+2] = ui.bgColor.b;
+ px[i+3] = 255;
+ }
+ }
+ }
+ ExportImage(img, screenshotFile);
+ UnloadImage(img);
+ UnloadRenderTexture(target);
+ screenshotFile = NULL;
+ }
+ }
}
/* ------------------------------------------------------------------ */
@@ -125,6 +174,11 @@ int main(void)
InitAudioDevice();
SetTargetFPS(60);
+ /* --- Screenshot mode (env-controlled, for automated testing) --- */
+ screenshotFile = getenv("STUDY_PLAYER_SCREENSHOT");
+ if (screenshotFile)
+ MaximizeWindow(); /* force window to be mapped/visible */
+
ui_init(&ui);
memset(&state, 0, sizeof(state));
@@ -156,8 +210,11 @@ int main(void)
#ifdef PLATFORM_WEB
emscripten_set_main_loop(update_frame, 0, 1);
#else
- while (!WindowShouldClose())
+ while (!WindowShouldClose()) {
update_frame();
+ if (screenshotFile == NULL && screenshotFrameCount >= SCREENSHOT_DELAY_FRAMES)
+ break; /* screenshot taken, exit */
+ }
#endif
player_unload(&state);
diff --git a/src/ui.c b/src/ui.c
index 7d6a808..600c368 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -426,34 +426,6 @@ void ui_render_player(const UIState *ui, const PlayerState *state,
float ty = smartBtn.y + (smartBtn.height - ui->szSmall) / 2.0f;
DrawTextEx(ui->fontSmall, "Play", (Vector2){ tx, ty }, ui->szSmall, btnSpacing, btnTextColor);
}
-
- /* --- Help text --- */
- {
- float helpSpacing = ui->szHelp * 0.03f;
- DrawTextEx(ui->fontHelp,
- "C: pause N: play Space(hold): override V/B: prev/next Arrows: seek 0-9: jump",
- (Vector2){ layout->helpX, layout->helpY },
- ui->szHelp, helpSpacing, ui->mutedColor);
- }
-
- /* --- Study mode checkbox --- */
- {
- float helpSpacing = ui->szHelp * 0.03f;
- const char *label = "Study Mode";
- float cbSize = 30.0f;
- Vector2 labelSize = MeasureTextEx(ui->fontHelp, label, ui->szHelp, helpSpacing);
- float totalW = cbSize + 10 + labelSize.x;
- float cbX = SCREEN_W - totalW - layout->helpX;
- float cbY = layout->helpY + (ui->szHelp - cbSize) / 2.0f;
-
- Rectangle cbRect = { cbX, cbY, cbSize, cbSize };
- DrawRectangleLinesEx(cbRect, 2, ui->mutedColor);
- if (state->studyMode)
- DrawRectangleRec((Rectangle){ cbX + 6, cbY + 6, cbSize - 12, cbSize - 12 }, ui->accentColor);
- DrawTextEx(ui->fontHelp, label,
- (Vector2){ cbX + cbSize + 10, layout->helpY },
- ui->szHelp, helpSpacing, ui->mutedColor);
- }
}
void ui_render_empty(const UIState *ui, const UILayout *layout)
@@ -468,3 +440,30 @@ void ui_render_empty(const UIState *ui, const UILayout *layout)
(float)SCREEN_W / 2.0f, SCREEN_H / 2.0f - 20, ui->szMed, ui->mutedColor);
#endif
}
+
+void ui_render_overlay(const UIState *ui, const PlayerState *state,
+ const UILayout *layout)
+{
+ /* --- Help text --- */
+ float helpSpacing = ui->szHelp * 0.03f;
+ DrawTextEx(ui->fontHelp,
+ "C: pause N: play Space(hold): override V/B: prev/next Arrows: seek 0-9: jump",
+ (Vector2){ layout->helpX, layout->helpY },
+ ui->szHelp, helpSpacing, ui->mutedColor);
+
+ /* --- Study mode checkbox --- */
+ const char *label = "Study Mode";
+ float cbSize = 30.0f;
+ Vector2 labelSize = MeasureTextEx(ui->fontHelp, label, ui->szHelp, helpSpacing);
+ float totalW = cbSize + 10 + labelSize.x;
+ float cbX = SCREEN_W - totalW - layout->helpX;
+ float cbY = layout->helpY + (ui->szHelp - cbSize) / 2.0f;
+
+ Rectangle cbRect = { cbX, cbY, cbSize, cbSize };
+ DrawRectangleLinesEx(cbRect, 2, ui->mutedColor);
+ if (state->studyMode)
+ DrawRectangleRec((Rectangle){ cbX + 6, cbY + 6, cbSize - 12, cbSize - 12 }, ui->accentColor);
+ DrawTextEx(ui->fontHelp, label,
+ (Vector2){ cbX + cbSize + 10, layout->helpY },
+ ui->szHelp, helpSpacing, ui->mutedColor);
+}
diff --git a/src/ui.h b/src/ui.h
index 10b4240..d6d0b24 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -60,3 +60,8 @@ void ui_render_player(const UIState *ui, const PlayerState *state,
/* Draw the "no file loaded" splash screen. */
void ui_render_empty(const UIState *ui, const UILayout *layout);
+
+/* Draw the help text and study-mode checkbox (always visible on tab 0).
+ * Call after ui_render_player or ui_render_empty. */
+void ui_render_overlay(const UIState *ui, const PlayerState *state,
+ const UILayout *layout);