refactor: centralize screen layout in ComputeLayout()

The spectrogram layout metrics (sidebar width, margins, freq-label width,
scrollbar sizes, spectroHeight, viewBounds) were computed with identical
formulas in three places — the input, selection, and render passes — so a
layout tweak meant editing all three in sync (as the scope divider change
just did). Introduce a Layout struct + ComputeLayout(); each site now
unpacks from it into its existing locals, leaving every downstream
coordinate reference untouched. Verified pixel-identical to the previous
build (ImageMagick AE = 0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 02:05:19 -07:00
parent 487e3ad85b
commit dd0ad9506a
+70 -48
View File
@@ -62,6 +62,43 @@ static float ScopeDivider(void)
return app.showScope ? app.dividerY : 1.0f;
}
// Screen layout metrics, derived from window size + UI scale. Single source of
// truth: the input, selection, and render passes all unpack from this so the
// layout formulas live in exactly one place.
typedef struct {
float scale;
float sidebarWidth;
float labelHeight;
float scrollbarHeight;
float freqLabelWidth;
float vScrollbarWidth;
float topMargin;
float bottomMargin;
float spectroHeight; // height of the spectrogram (respects the scope divider)
Rectangle viewBounds; // the spectrogram drawing area
} Layout;
static Layout ComputeLayout(void)
{
Layout L;
L.scale = GetUIScale();
L.sidebarWidth = 320 * L.scale;
L.labelHeight = 15 * L.scale;
L.scrollbarHeight = 18 * L.scale;
L.freqLabelWidth = 65 * L.scale;
L.vScrollbarWidth = 18 * L.scale;
L.topMargin = 50 * L.scale;
L.bottomMargin = 10 * L.scale;
L.spectroHeight = (GetScreenHeight() - L.topMargin - L.bottomMargin - L.labelHeight - L.scrollbarHeight - 10 * L.scale) * ScopeDivider();
L.viewBounds = (Rectangle){
L.sidebarWidth + L.freqLabelWidth,
L.topMargin,
GetScreenWidth() - L.sidebarWidth - L.freqLabelWidth - L.vScrollbarWidth - 20 * L.scale,
L.spectroHeight
};
return L;
}
// Reset all per-signal state after a new signal has been loaded into app.signal.
// Drops the cached STFT/FFT-size cache and the on-screen textures so the main
// loop recomputes from scratch (loadingPhase 0 handles the STFT (re)alloc).
@@ -260,22 +297,17 @@ int main(int argc, char* argv[])
// View controls
if (app.loaded && !UiModalOpen()) {
// 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;
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * viewScale) * ScopeDivider();
Rectangle viewBounds = {
sidebarWidth + freqLabelWidth,
topMargin,
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20 * viewScale,
spectroHeight
};
Layout L = ComputeLayout();
float viewScale = L.scale;
float sidebarWidth = L.sidebarWidth;
float labelHeight = L.labelHeight;
float scrollbarHeight = L.scrollbarHeight;
float freqLabelWidth = L.freqLabelWidth;
float vScrollbarWidth = L.vScrollbarWidth;
float topMargin = L.topMargin;
float bottomMargin = L.bottomMargin;
float spectroHeight = L.spectroHeight;
Rectangle viewBounds = L.viewBounds;
// Zoom with mouse wheel (zooms both time and frequency to maintain aspect ratio)
if (GetMousePosition().x > sidebarWidth + 5 && CheckCollisionPointRec(GetMousePosition(), viewBounds)) {
@@ -478,22 +510,17 @@ int main(int argc, char* argv[])
}
// Selection: box select with LMB drag, right-click to clear
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;
float selSpectroHeight = (GetScreenHeight() - selTopMargin - selBottomMargin - selLabelHeight - selScrollbarHeight - 10.0f * selScale) * ScopeDivider();
Rectangle selBounds = {
selSidebarWidth + selFreqLabelWidth,
selTopMargin,
GetScreenWidth() - selSidebarWidth - selFreqLabelWidth - selVScrollbarWidth - 20.0f * selScale,
selSpectroHeight
};
Layout selL = ComputeLayout();
float selScale = selL.scale;
float selSidebarWidth = selL.sidebarWidth;
float selLabelHeight = selL.labelHeight;
float selScrollbarHeight = selL.scrollbarHeight;
float selFreqLabelWidth = selL.freqLabelWidth;
float selVScrollbarWidth = selL.vScrollbarWidth;
float selTopMargin = selL.topMargin;
float selBottomMargin = selL.bottomMargin;
float selSpectroHeight = selL.spectroHeight;
Rectangle selBounds = selL.viewBounds;
Vector2 mousePos = GetMousePosition();
// Calculate divider screen position (for hover detection)
@@ -766,23 +793,18 @@ int main(int argc, char* argv[])
ClearBackground((Color){ 30, 30, 30, 255 });
// 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)
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * renderScale) * ScopeDivider();
Rectangle viewBounds = {
sidebarWidth + freqLabelWidth,
topMargin,
GetScreenWidth() - sidebarWidth - freqLabelWidth - vScrollbarWidth - 20 * renderScale,
spectroHeight
};
Layout L = ComputeLayout();
float renderScale = L.scale;
float sidebarWidth = L.sidebarWidth;
float labelHeight = L.labelHeight;
float scrollbarHeight = L.scrollbarHeight;
float freqLabelWidth = L.freqLabelWidth;
float vScrollbarWidth = L.vScrollbarWidth;
float topMargin = L.topMargin;
float bottomMargin = L.bottomMargin;
float spectroHeight = L.spectroHeight;
Rectangle viewBounds = L.viewBounds;
// 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