Add FLAG_WINDOW_RESIZABLE, fullscreen button, and proportional UI scaling
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
+70
-45
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user