feat: scope toggle collapses the divider; drag handle to show/hide
Toggling the scope off (P) now drops the divider to the bottom so the spectrogram fills the whole view, instead of leaving an empty gap. The divider handle is always drawn and grabbable: drag it up from the bottom to bring the scope back, or drag it down past ~88% to hide it. Layout uses an effective-divider helper (1.0 when hidden) so all three layout sites stay in sync. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+38
-22
@@ -53,6 +53,15 @@ static bool IsUserInteracting(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fraction of the view area used by the spectrogram. When the scope is hidden
|
||||||
|
// the spectrogram fills the whole area (divider at the bottom); otherwise the
|
||||||
|
// scope takes the remainder below dividerY.
|
||||||
|
#define SCOPE_COLLAPSE_DIVIDER 0.88f // drag the handle past this to hide the scope
|
||||||
|
static float ScopeDivider(void)
|
||||||
|
{
|
||||||
|
return app.showScope ? app.dividerY : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset all per-signal state after a new signal has been loaded into app.signal.
|
// 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
|
// 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).
|
// loop recomputes from scratch (loadingPhase 0 handles the STFT (re)alloc).
|
||||||
@@ -260,7 +269,7 @@ int main(int argc, char* argv[])
|
|||||||
float topMargin = 50 * viewScale;
|
float topMargin = 50 * viewScale;
|
||||||
float bottomMargin = 10 * viewScale;
|
float bottomMargin = 10 * viewScale;
|
||||||
|
|
||||||
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * viewScale) * app.dividerY;
|
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * viewScale) * ScopeDivider();
|
||||||
Rectangle viewBounds = {
|
Rectangle viewBounds = {
|
||||||
sidebarWidth + freqLabelWidth,
|
sidebarWidth + freqLabelWidth,
|
||||||
topMargin,
|
topMargin,
|
||||||
@@ -478,7 +487,7 @@ int main(int argc, char* argv[])
|
|||||||
float selTopMargin = 50 * selScale;
|
float selTopMargin = 50 * selScale;
|
||||||
float selBottomMargin = 10 * selScale;
|
float selBottomMargin = 10 * selScale;
|
||||||
|
|
||||||
float selSpectroHeight = (GetScreenHeight() - selTopMargin - selBottomMargin - selLabelHeight - selScrollbarHeight - 10.0f * selScale) * app.dividerY;
|
float selSpectroHeight = (GetScreenHeight() - selTopMargin - selBottomMargin - selLabelHeight - selScrollbarHeight - 10.0f * selScale) * ScopeDivider();
|
||||||
Rectangle selBounds = {
|
Rectangle selBounds = {
|
||||||
selSidebarWidth + selFreqLabelWidth,
|
selSidebarWidth + selFreqLabelWidth,
|
||||||
selTopMargin,
|
selTopMargin,
|
||||||
@@ -628,28 +637,28 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle divider drag
|
// Handle divider drag. Works whether or not the scope is currently shown
|
||||||
if (app.loaded && app.showScope && !app.showFileBrowser && !app.showAbout) {
|
// (when hidden, the handle sits at the bottom and can be dragged back up).
|
||||||
// Calculate divider position (using same calculation as selBounds)
|
if (app.loaded && !app.showFileBrowser && !app.showAbout) {
|
||||||
float viewScale = GetUIScale();
|
// Grab the handle. The starting position is the *effective* divider,
|
||||||
float topMargin = 50 * viewScale;
|
// which is the bottom of the view (1.0) while the scope is hidden.
|
||||||
float bottomMargin = 10 * viewScale;
|
|
||||||
float labelHeight = 15 * viewScale;
|
|
||||||
float scrollbarHeight = 18 * viewScale;
|
|
||||||
float totalHeight = GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight;
|
|
||||||
|
|
||||||
// Check if mouse is near divider
|
|
||||||
if (mouseNearDivider && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
if (mouseNearDivider && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
|
||||||
app.isDividing = true;
|
app.isDividing = true;
|
||||||
app.dividerStartPos = mousePos;
|
app.dividerStartPos = mousePos;
|
||||||
app.dividerStartY = app.dividerY;
|
app.dividerStartY = ScopeDivider();
|
||||||
}
|
}
|
||||||
if (app.isDividing && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
|
if (app.isDividing && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
|
||||||
float dy = (mousePos.y - app.dividerStartPos.y) / GetScreenHeight();
|
float d = app.dividerStartY + (mousePos.y - app.dividerStartPos.y) / GetScreenHeight();
|
||||||
app.dividerY = app.dividerStartY + dy;
|
if (d >= SCOPE_COLLAPSE_DIVIDER) {
|
||||||
// Clamp to reasonable range: 0.3 to 0.8 (30% to 80% for spectrogram)
|
// Dragged (almost) to the bottom — hide the scope.
|
||||||
if (app.dividerY < 0.3f) app.dividerY = 0.3f;
|
app.showScope = false;
|
||||||
if (app.dividerY > 0.8f) app.dividerY = 0.8f;
|
} else {
|
||||||
|
// Otherwise the scope is shown; clamp the split to 30%..80%.
|
||||||
|
if (d < 0.3f) d = 0.3f;
|
||||||
|
if (d > 0.8f) d = 0.8f;
|
||||||
|
app.showScope = true;
|
||||||
|
app.dividerY = d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
|
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
|
||||||
app.isDividing = false;
|
app.isDividing = false;
|
||||||
@@ -767,7 +776,7 @@ int main(int argc, char* argv[])
|
|||||||
float bottomMargin = 10 * renderScale;
|
float bottomMargin = 10 * renderScale;
|
||||||
|
|
||||||
// Spectrogram area (excludes labels and scrollbars)
|
// Spectrogram area (excludes labels and scrollbars)
|
||||||
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * renderScale) * app.dividerY;
|
float spectroHeight = (GetScreenHeight() - topMargin - bottomMargin - labelHeight - scrollbarHeight - 10 * renderScale) * ScopeDivider();
|
||||||
Rectangle viewBounds = {
|
Rectangle viewBounds = {
|
||||||
sidebarWidth + freqLabelWidth,
|
sidebarWidth + freqLabelWidth,
|
||||||
topMargin,
|
topMargin,
|
||||||
@@ -933,8 +942,9 @@ int main(int argc, char* argv[])
|
|||||||
DrawTextScaled("Waveform (Time/Amplitude)", viewBounds.x, app.scopeView.y - 20, 14, LIGHTGRAY);
|
DrawTextScaled("Waveform (Time/Amplitude)", viewBounds.x, app.scopeView.y - 20, 14, LIGHTGRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw divider line between spectrogram and scope
|
// Draw divider line + handle. Always shown (even when the scope is
|
||||||
if (app.showScope && app.loaded) {
|
// hidden) so the handle can be grabbed at the bottom to bring it back.
|
||||||
|
if (app.loaded) {
|
||||||
float dividerY = viewBounds.y + viewBounds.height;
|
float dividerY = viewBounds.y + viewBounds.height;
|
||||||
Color dividerColor = app.isDividing ? CYAN : Fade((Color){ 180, 180, 200, 255 }, 0.7f);
|
Color dividerColor = app.isDividing ? CYAN : Fade((Color){ 180, 180, 200, 255 }, 0.7f);
|
||||||
|
|
||||||
@@ -963,6 +973,12 @@ int main(int argc, char* argv[])
|
|||||||
// Draw line extending from handle to edges
|
// Draw line extending from handle to edges
|
||||||
DrawLine(viewBounds.x, (int)dividerY, handleX, (int)dividerY, dividerColor);
|
DrawLine(viewBounds.x, (int)dividerY, handleX, (int)dividerY, dividerColor);
|
||||||
DrawLine(handleX + handleW, (int)dividerY, viewBounds.x + viewBounds.width, (int)dividerY, dividerColor);
|
DrawLine(handleX + handleW, (int)dividerY, viewBounds.x + viewBounds.width, (int)dividerY, dividerColor);
|
||||||
|
|
||||||
|
// Hint that the hidden scope can be dragged back up.
|
||||||
|
if (!app.showScope && !app.isDividing) {
|
||||||
|
DrawTextScaled("drag up for scope", handleX + handleW + 8, (int)dividerY - 7, 11,
|
||||||
|
Fade(LIGHTGRAY, 0.6f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (!app.showFileBrowser) {
|
} else if (!app.showFileBrowser) {
|
||||||
const char* msg1 = "Press 'O' or click 'Open File Browser' to load a WAV";
|
const char* msg1 = "Press 'O' or click 'Open File Browser' to load a WAV";
|
||||||
|
|||||||
Reference in New Issue
Block a user