Add FLAG_WINDOW_RESIZABLE, fullscreen button, and proportional UI scaling

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-04-15 15:40:17 -07:00
parent da37cecb59
commit 9d0008ac2c
+70 -45
View File
@@ -34,6 +34,18 @@
#define MAX_SAMPLE_RATE 48000
#define LOUDNESS_FLOOR_DB -80.0f
// Base resolution for proportional UI scaling
#define BASE_WIDTH 1280
#define BASE_HEIGHT 800
// Get uniform scale factor based on current window size
static float GetUIScale(void)
{
float scaleX = (float)GetScreenWidth() / BASE_WIDTH;
float scaleY = (float)GetScreenHeight() / BASE_HEIGHT;
return (scaleX + scaleY) / 2.0f;
}
// Colormap types
typedef enum {
COLORMAP_GRAYS = 0,
@@ -568,15 +580,15 @@ static void ApplyBandpassFilterFFT(float* samples, int numSamples, int sampleRat
while (fftSize < numSamples) fftSize *= 2;
fftSize *= 2;
float* windowedSamples = (float*)calloc(fftSize, sizeof(float));
float* paddedSamples = (float*)calloc(fftSize, sizeof(float));
float complex *fftInput = (float complex*)malloc(fftSize * sizeof(float complex));
float complex *fftOutput = (float complex*)malloc(fftSize * sizeof(float complex));
// Copy samples directly without windowing (windowing causes fade in/out)
for (int i = 0; i < numSamples; i++) {
float window = 0.5f * (1.0f - cosf(2.0f * M_PI * i / (numSamples - 1)));
windowedSamples[i] = samples[i] * window;
paddedSamples[i] = samples[i];
}
for (int i = 0; i < fftSize; i++) fftInput[i] = windowedSamples[i] + 0.0f * I;
for (int i = 0; i < fftSize; i++) fftInput[i] = paddedSamples[i] + 0.0f * I;
FFT(fftInput, fftOutput, fftSize, false);
@@ -615,7 +627,7 @@ static void ApplyBandpassFilterFFT(float* samples, int numSamples, int sampleRat
}
}
free(windowedSamples); free(fftInput); free(fftOutput); free(ifftOutput);
free(paddedSamples); free(fftInput); free(fftOutput); free(ifftOutput);
}
static void ApplyBandpassFilter(float* samples, int numSamples, int sampleRate, float freqLow, float freqHigh)
@@ -1010,18 +1022,19 @@ static void DrawSelectionDrag(Rectangle bounds)
static void DrawSidebar(void)
{
float sidebarWidth = 300;
float x = 10;
float y = 10;
int fontSize = 12;
float scale = GetUIScale();
float sidebarWidth = 300 * scale;
float x = 10 * scale;
float y = 10 * scale;
int fontSize = (int)(12 * scale);
bool needsRegen = false;
// Dark sidebar background
DrawRectangle(0, 0, sidebarWidth + 20, GetScreenHeight(), (Color){ 35, 35, 40, 255 });
DrawLine(sidebarWidth + 10, 0, sidebarWidth + 10, GetScreenHeight(), GRAY);
DrawRectangle(0, 0, (int)(sidebarWidth + 20 * scale), GetScreenHeight(), (Color){ 35, 35, 40, 255 });
DrawLine((int)(sidebarWidth + 10 * scale), 0, (int)(sidebarWidth + 10 * scale), GetScreenHeight(), GRAY);
// Title
DrawText("Spectrogram Controls", x, y, 14, WHITE); y += 25;
DrawText("Spectrogram Controls", x, y, (int)(14 * scale), WHITE); y += 25 * scale;
// FFT Size clicker
DrawText(TextFormat("FFT Size: %d (%.1f Hz/bin)", app.fftSize, (float)app.signal.sampleRate / app.fftSize), x, y, fontSize, LIGHTGRAY); y += 18;
@@ -1109,6 +1122,17 @@ static void DrawSidebar(void)
DrawText(playText, playButton.x + 10, playButton.y + 12, fontSize, WHITE);
y += 45;
// Fullscreen toggle
Rectangle fsButton = { x, y, sidebarWidth - 10, 25 };
bool isFullscreen = IsWindowFullscreen();
if (CheckCollisionPointRec(GetMousePosition(), fsButton) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
ToggleFullscreen();
}
DrawRectangleRec(fsButton, isFullscreen ? (Color){ 40, 80, 120, 255 } : (Color){ 50, 50, 60, 255 });
DrawRectangleLinesEx(fsButton, 1, GRAY);
DrawText(isFullscreen ? "Exit Fullscreen (F11)" : "Fullscreen (F11)", (int)(fsButton.x + 10 * scale), (int)(fsButton.y + 6 * scale), fontSize, WHITE);
y += 35;
// Reset/Clear buttons
Rectangle resetButton = { x, y, (sidebarWidth - 15) / 2, 25 };
if (CheckCollisionPointRec(GetMousePosition(), resetButton) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
@@ -1184,7 +1208,7 @@ static bool UpdateSlider(Rectangle bounds, float* value)
int main(int argc, char* argv[])
{
SetConfigFlags(FLAG_VSYNC_HINT);
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE);
InitWindow(1280, 800, "Spectrogram Viewer");
SetTargetFPS(60);
SetTraceLogLevel(LOG_WARNING); // Suppress INFO texture logs
@@ -1279,25 +1303,25 @@ int main(int argc, char* argv[])
// View controls
if (app.loaded && !app.showFileBrowser) {
// Spectrogram area fills remaining window space
// Space for: spectrogram + time labels (15px below) + scrollbar (18px below that)
float sidebarWidth = 320;
float labelHeight = 15;
float scrollbarHeight = 18;
float freqLabelWidth = 65;
float vScrollbarWidth = 18;
float topMargin = 50;
float bottomMargin = 10;
// Spectrogram area fills remaining window space (scaled)
float viewScale = GetUIScale();
float sidebarWidth = 320 * viewScale;
float labelHeight = 15 * viewScale;
float scrollbarHeight = 18 * viewScale;
float freqLabelWidth = 65 * viewScale;
float vScrollbarWidth = 18 * viewScale;
float topMargin = 50 * viewScale;
float bottomMargin = 10 * viewScale;
Rectangle viewBounds = {
sidebarWidth + freqLabelWidth,
topMargin,
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20,
GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20 * viewScale,
GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * viewScale
};
// Zoom with mouse wheel (zooms both time and frequency to maintain aspect ratio)
if (GetMousePosition().x > 385 && CheckCollisionPointRec(GetMousePosition(), viewBounds)) {
if (GetMousePosition().x > sidebarWidth + 5 && CheckCollisionPointRec(GetMousePosition(), viewBounds)) {
int wheel = GetMouseWheelMove();
if (wheel != 0) {
float zoomFactor = (wheel > 0) ? 0.8f : 1.2f;
@@ -1408,13 +1432,14 @@ int main(int argc, char* argv[])
}
// Selection: box select with LMB drag, right-click to clear
float selSidebarWidth = 320;
float selLabelHeight = 15;
float selScrollbarHeight = 18;
float selFreqLabelWidth = 65;
float selVScrollbarWidth = 18;
float selTopMargin = 50;
float selBottomMargin = 10;
float selScale = GetUIScale();
float selSidebarWidth = 320 * selScale;
float selLabelHeight = 15 * selScale;
float selScrollbarHeight = 18 * selScale;
float selFreqLabelWidth = 65 * selScale;
float selVScrollbarWidth = 18 * selScale;
float selTopMargin = 50 * selScale;
float selBottomMargin = 10 * selScale;
Rectangle selBounds = {
selSidebarWidth + selFreqLabelWidth,
@@ -1564,29 +1589,29 @@ int main(int argc, char* argv[])
BeginDrawing();
ClearBackground((Color){ 30, 30, 30, 255 });
// Layout: sidebar on left, spectrogram on right
// Space for: spectrogram + time labels (15px below) + scrollbar (18px below that)
float sidebarWidth = 320;
float labelHeight = 15;
float scrollbarHeight = 18;
float freqLabelWidth = 65;
float vScrollbarWidth = 18;
float topMargin = 50;
float bottomMargin = 10;
// Layout: sidebar on left, spectrogram on right (scaled)
float renderScale = GetUIScale();
float sidebarWidth = 320 * renderScale;
float labelHeight = 15 * renderScale;
float scrollbarHeight = 18 * renderScale;
float freqLabelWidth = 65 * renderScale;
float vScrollbarWidth = 18 * renderScale;
float topMargin = 50 * renderScale;
float bottomMargin = 10 * renderScale;
// Spectrogram area (excludes labels and scrollbars)
Rectangle viewBounds = {
sidebarWidth + freqLabelWidth,
topMargin,
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20,
GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20 * renderScale,
GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * renderScale
};
// Time labels sit just below the spectrogram
Rectangle timeLabelArea = { viewBounds.x, viewBounds.y + viewBounds.height, viewBounds.width, labelHeight };
// Horizontal scrollbar sits below the time labels
Rectangle hScrollbar = { viewBounds.x, viewBounds.y + viewBounds.height + labelHeight + 5, viewBounds.width, scrollbarHeight };
Rectangle hScrollbar = { viewBounds.x, viewBounds.y + viewBounds.height + labelHeight + 5 * renderScale, viewBounds.width, scrollbarHeight };
// Vertical scrollbar sits to the right of the spectrogram
Rectangle vScrollbar = { viewBounds.x + viewBounds.width + 5, viewBounds.y, vScrollbarWidth, viewBounds.height };
Rectangle vScrollbar = { viewBounds.x + viewBounds.width + 5 * renderScale, viewBounds.y, vScrollbarWidth, viewBounds.height };
// Draw sidebar first (on top left)
DrawSidebar();