Files
soundThing/include/ui.h
Anachronaut e40645f280 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>
2026-07-07 22:32:46 -04:00

112 lines
3.8 KiB
C

#ifndef UI_H
#define UI_H
#include "raylib.h"
#include "synth.h"
#include "midi.h"
#include "patch.h"
#include "config.h"
typedef struct {
int id;
float *valuePtr;
float min;
float max;
} UIControlEntry;
// Sprite indices
typedef enum {
SPRITE_WAVE_SINE = 0,
SPRITE_WAVE_TRIANGLE,
SPRITE_WAVE_SAW,
SPRITE_WAVE_RAMP,
SPRITE_WAVE_PULSE,
SPRITE_WAVE_NOISE,
SPRITE_BTN_RED,
SPRITE_BTN_GREEN,
SPRITE_SLIDER,
SPRITE_KNOB
} SpriteIndex;
// UI state that needs to persist between frames
typedef struct {
Texture2D sprites;
Font font;
MidiState *midi;
Camera2D *camera;
// About menu state
int aboutMenuOpen;
// MIDI menu state
int midiMenuOpen;
int midiMenuSelected; // -1 if none
// Knob drag state
int draggingKnob; // -1 if none
Vector2 dragStart;
float dragStartValue;
// Slider drag state
int draggingSlider; // -1 if none
// MIDI learn state
int midiLearnActive; // 0=off 1=click a control 2=wiggle CC
int midiLearnTargetId; // id of selected control (-1 if none)
float *midiLearnValuePtr;
float midiLearnMin;
float midiLearnMax;
// Patch browser state
int patchMenuOpen;
int focusedTextInput; // -1 if none
int patchConfirmOverwrite;
int patchConfirmClear;
char patchSaveName[PATCH_NAME_LEN];
char patchCurrentName[PATCH_NAME_LEN];
int patchFileCount;
char patchFiles[PATCH_MAX_FILES][PATCH_NAME_LEN];
int patchScrollOffset;
int patchShowFavsOnly;
char patchFavs[PATCH_MAX_FILES][PATCH_NAME_LEN];
int patchFavCount;
char patchCurrentDir[256];
char patchSubDirs[PATCH_MAX_DIRS][PATCH_NAME_LEN];
int patchSubDirCount;
int patchVCursor;
// Control registry — built lazily by uiKnob/uiSlider for CC mapping persistence
UIControlEntry controlRegistry[128];
int controlRegistryCount;
// CC mappings loaded from file but not yet resolved (registry not yet populated)
ConfigCcEntry pendingMappings[128];
int pendingCount;
} UIState;
// Lifecycle
void uiInit(UIState *ui, MidiState *midi, Camera2D *camera);
void uiClose(UIState *ui);
// Primitives
float uiKnob(UIState *ui, int id, float x, float y, float *ptr, float min, float max, const char *label);
float uiSlider(UIState *ui, int id, float x, float y, float width, float *ptr, float min, float max, const char *label);
int uiWaveformSelector(UIState *ui, float x, float y, int currentWaveform);
void uiADSRShape(float x, float y, float width, float height, float attack, float decay, float sustain, float release, Color color);
void uiVoiceMeter(UIState *ui, float x, float y, Voice *voices, int voiceCount);
// Panels
void uiOscillatorPanel(UIState *ui, int baseId, float x, float y, float width, float height, Oscillator *osc, const char *title);
void uiEnvelopePanel(UIState *ui, int baseId, float x, float y, float width, float height, Envelope *env, const char *title);
void uiLfoPanel(UIState *ui, int baseId, float x, float y, float width, float height, LFO *lfo, const char *title);
void uiFilterPanel(UIState *ui, float x, float y, float width, float height, Filter *filter, const char *title);
void uiMasterPanel(UIState *ui, float x, float y, float width, float height, Synth *s);
void uiMidiMenu(UIState *ui, float x, float y, float width, MidiState *midi);
void uiPatchMenu(UIState *ui, float x, float y, float width, Synth *s);
void uiAboutMenu(UIState *ui, float x, float y, float width);
void uiLoadCcMappingsForDevice(UIState *ui, const char *deviceName);
// Window helpers
void recomputeViewPort(Camera2D* camera, int x_logical_resolution, int y_logical_resolution, int border_width);
#endif