Fix patch browser keyboard navigation to cover directory rows
Arrow keys now navigate the full virtual row list (../, subdirs, patches) rather than only patch rows. Landing on a patch auto-loads it as before; landing on .. or a subdir highlights it and Enter confirms navigation. Added patchVCursor to UIState to track keyboard focus independently of the loaded patch. Mouse-click on a patch now also syncs the cursor so subsequent arrow-key movement starts from the right position. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
100
source/ui.c
100
source/ui.c
@@ -137,6 +137,7 @@ void uiInit(UIState *ui, MidiState *midi, Camera2D *camera)
|
||||
ui->patchFavCount = patchLoadFavs(ui->patchFavs, PATCH_MAX_FILES);
|
||||
ui->patchCurrentDir[0] = '\0';
|
||||
ui->patchSubDirCount = 0;
|
||||
ui->patchVCursor = -1;
|
||||
ui->controlRegistryCount = 0;
|
||||
ui->pendingCount = 0;
|
||||
}
|
||||
@@ -827,6 +828,7 @@ static void patchRescan(UIState *ui)
|
||||
ui->patchFiles, PATCH_MAX_FILES,
|
||||
ui->patchSubDirs, PATCH_MAX_DIRS, &ui->patchSubDirCount);
|
||||
ui->patchScrollOffset = 0;
|
||||
ui->patchVCursor = -1;
|
||||
}
|
||||
|
||||
void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s)
|
||||
@@ -1042,32 +1044,69 @@ void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s)
|
||||
ui->patchScrollOffset -= (int)GetMouseWheelMove();
|
||||
}
|
||||
|
||||
// Arrow key navigation: move selection through patches only
|
||||
// Arrow key navigation over all virtual rows: [..], subdirs, patches
|
||||
if (ui->focusedTextInput == -1 && !ui->patchConfirmOverwrite && !ui->patchConfirmClear
|
||||
&& filteredCount > 0) {
|
||||
int curFi = -1;
|
||||
for (int fi = 0; fi < filteredCount; fi++) {
|
||||
if (strcmp(ui->patchFiles[filteredIdx[fi]], ui->patchCurrentName) == 0) {
|
||||
curFi = fi;
|
||||
break;
|
||||
&& totalVirtual > 0) {
|
||||
// Sync cursor from currently loaded patch if unset
|
||||
if (ui->patchVCursor < 0 && ui->patchCurrentName[0]) {
|
||||
for (int fi = 0; fi < filteredCount; fi++) {
|
||||
if (strcmp(ui->patchFiles[filteredIdx[fi]], ui->patchCurrentName) == 0) {
|
||||
ui->patchVCursor = fi + backRows + ui->patchSubDirCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int nextFi = curFi;
|
||||
if (IsKeyPressed(KEY_DOWN) && curFi < filteredCount - 1) nextFi = curFi + 1;
|
||||
if (IsKeyPressed(KEY_UP) && curFi > 0) nextFi = curFi - 1;
|
||||
if (nextFi != curFi) {
|
||||
int i = filteredIdx[nextFi];
|
||||
char path[512];
|
||||
buildPatchPath(path, sizeof(path), ui->patchCurrentDir, ui->patchFiles[i]);
|
||||
if (patchLoad(s, path)) {
|
||||
snprintf(ui->patchCurrentName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
snprintf(ui->patchSaveName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
int next = ui->patchVCursor;
|
||||
if (IsKeyPressed(KEY_DOWN))
|
||||
next = (next < 0) ? 0 : (next < totalVirtual - 1 ? next + 1 : next);
|
||||
if (IsKeyPressed(KEY_UP))
|
||||
next = (next < 0) ? totalVirtual - 1 : (next > 0 ? next - 1 : next);
|
||||
if (next != ui->patchVCursor) {
|
||||
ui->patchVCursor = next;
|
||||
// Auto-load when landing on a patch row
|
||||
if (next >= backRows + ui->patchSubDirCount && filteredCount > 0) {
|
||||
int fi = next - backRows - ui->patchSubDirCount;
|
||||
int i = filteredIdx[fi];
|
||||
char path[512];
|
||||
buildPatchPath(path, sizeof(path), ui->patchCurrentDir, ui->patchFiles[i]);
|
||||
if (patchLoad(s, path)) {
|
||||
snprintf(ui->patchCurrentName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
snprintf(ui->patchSaveName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
}
|
||||
}
|
||||
int vRow = nextFi + backRows + ui->patchSubDirCount;
|
||||
if (vRow < ui->patchScrollOffset)
|
||||
ui->patchScrollOffset = vRow;
|
||||
if (vRow >= ui->patchScrollOffset + MAX_VIS)
|
||||
ui->patchScrollOffset = vRow - MAX_VIS + 1;
|
||||
}
|
||||
// Enter activates navigation rows (.. and subdirs)
|
||||
if (IsKeyPressed(KEY_ENTER) && ui->patchVCursor >= 0) {
|
||||
int vc = ui->patchVCursor;
|
||||
if (!isRoot && vc == 0) {
|
||||
char *lastSlash = strrchr(ui->patchCurrentDir, '/');
|
||||
if (lastSlash) *lastSlash = '\0';
|
||||
else ui->patchCurrentDir[0] = '\0';
|
||||
ui->patchCurrentName[0] = '\0';
|
||||
patchRescan(ui);
|
||||
return;
|
||||
} else if (vc >= backRows && vc < backRows + ui->patchSubDirCount) {
|
||||
int d = vc - backRows;
|
||||
if (ui->patchCurrentDir[0]) {
|
||||
size_t curLen = strlen(ui->patchCurrentDir);
|
||||
snprintf(ui->patchCurrentDir + curLen,
|
||||
sizeof(ui->patchCurrentDir) - curLen,
|
||||
"/%s", ui->patchSubDirs[d]);
|
||||
} else {
|
||||
snprintf(ui->patchCurrentDir, sizeof(ui->patchCurrentDir),
|
||||
"%s", ui->patchSubDirs[d]);
|
||||
}
|
||||
ui->patchCurrentName[0] = '\0';
|
||||
patchRescan(ui);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Scroll to keep cursor visible
|
||||
if (ui->patchVCursor >= 0) {
|
||||
if (ui->patchVCursor < ui->patchScrollOffset)
|
||||
ui->patchScrollOffset = ui->patchVCursor;
|
||||
if (ui->patchVCursor >= ui->patchScrollOffset + MAX_VIS)
|
||||
ui->patchScrollOffset = ui->patchVCursor - MAX_VIS + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,10 +1138,10 @@ void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s)
|
||||
|
||||
if (!isRoot && r == 0) {
|
||||
// ".." — navigate to parent
|
||||
DrawRectangle((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2),
|
||||
(Color){20, 30, 50, 255});
|
||||
DrawRectangleLines((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2),
|
||||
(Color){80, 120, 180, 255});
|
||||
Color dotBg = (ui->patchVCursor == r) ? (Color){40, 60, 100, 255} : (Color){20, 30, 50, 255};
|
||||
Color dotEdge = (ui->patchVCursor == r) ? SKYBLUE : (Color){80, 120, 180, 255};
|
||||
DrawRectangle((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2), dotBg);
|
||||
DrawRectangleLines((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2), dotEdge);
|
||||
DRAW_TEXT("..", (int)(x + padding + 4), (int)(contentY + 2), fontSize, SKYBLUE);
|
||||
if (!ui->patchConfirmClear && IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
|
||||
mouse.x >= x + padding && mouse.x <= x + padding + btnW &&
|
||||
@@ -1119,10 +1158,10 @@ void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s)
|
||||
int d = r - backRows;
|
||||
char label[PATCH_NAME_LEN + 4];
|
||||
snprintf(label, sizeof(label), "> %s", ui->patchSubDirs[d]);
|
||||
DrawRectangle((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2),
|
||||
(Color){20, 30, 50, 255});
|
||||
DrawRectangleLines((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2),
|
||||
(Color){80, 120, 180, 255});
|
||||
Color dirBg = (ui->patchVCursor == r) ? (Color){40, 60, 100, 255} : (Color){20, 30, 50, 255};
|
||||
Color dirEdge = (ui->patchVCursor == r) ? SKYBLUE : (Color){80, 120, 180, 255};
|
||||
DrawRectangle((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2), dirBg);
|
||||
DrawRectangleLines((int)(x + padding), (int)contentY, (int)btnW, (int)(rowH - 2), dirEdge);
|
||||
DRAW_TEXT(label, (int)(x + padding + 4), (int)(contentY + 2), fontSize, SKYBLUE);
|
||||
if (!ui->patchConfirmClear && IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
|
||||
mouse.x >= x + padding && mouse.x <= x + padding + btnW &&
|
||||
@@ -1190,6 +1229,7 @@ void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s)
|
||||
if (patchLoad(s, path)) {
|
||||
snprintf(ui->patchCurrentName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
snprintf(ui->patchSaveName, PATCH_NAME_LEN, "%s", ui->patchFiles[i]);
|
||||
ui->patchVCursor = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user