diff options
| author | Adam Malczewski <[email protected]> | 2026-06-27 23:50:08 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-27 23:50:08 +0900 |
| commit | 7f0444bc308fef0b78b2fd4fa8aa269da619324c (patch) | |
| tree | 7b8072098df545192788cee0c44535e9c4f6e95c | |
| parent | 6bf8907cd69383e8bb0a77b741d314bddf8008e6 (diff) | |
| download | study-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.c | 59 | ||||
| -rw-r--r-- | src/ui.c | 55 | ||||
| -rw-r--r-- | src/ui.h | 5 |
3 files changed, 90 insertions, 29 deletions
@@ -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); @@ -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); +} @@ -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); |
