From c7bb89fd6da49cc4fdf4ce50ec53036663e31bd1 Mon Sep 17 00:00:00 2001 From: Max Amundsen Date: Thu, 12 Mar 2026 10:41:16 -0400 Subject: [PATCH] Format all --- .clang-format | 31 + src/audio/audio.h | 24 +- src/audio/audio_asio.cpp | 236 ++-- src/audio/audio_coreaudio.cpp | 105 +- src/base/base_arena.cpp | 6 +- src/base/base_arena.h | 10 +- src/base/base_core.h | 80 +- src/base/base_inc.h | 2 +- src/base/base_math.h | 81 +- src/base/base_strings.cpp | 8 +- src/base/base_strings.h | 6 +- src/main.cpp | 1784 ++++++++++++++---------------- src/menus.cpp | 30 +- src/midi/midi.h | 28 +- src/midi/midi_coremidi.cpp | 84 +- src/midi/midi_win32.cpp | 76 +- src/platform/platform.h | 60 +- src/platform/platform_macos.mm | 230 ++-- src/platform/platform_win32.cpp | 97 +- src/renderer/renderer.h | 22 +- src/renderer/renderer_vulkan.cpp | 1505 +++++++++++++------------ src/ui/ui_core.cpp | 157 ++- src/ui/ui_core.h | 72 +- src/ui/ui_icons.cpp | 14 +- src/ui/ui_icons.h | 4 +- src/ui/ui_piano.cpp | 80 +- src/ui/ui_piano.h | 8 +- src/ui/ui_popups.cpp | 62 +- src/ui/ui_popups.h | 28 +- src/ui/ui_widgets.cpp | 1517 ++++++++++++------------- src/ui/ui_widgets.h | 42 +- 31 files changed, 3289 insertions(+), 3200 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1fcba80 --- /dev/null +++ b/.clang-format @@ -0,0 +1,31 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +AlignConsecutiveDeclarations: true +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AlignConsecutiveMacros: true +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +Cpp11BracedListStyle: false +ColumnLimit: 0 diff --git a/src/audio/audio.h b/src/audio/audio.h index 7a49b8c..020a77b 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -5,19 +5,19 @@ struct AudioEngine; struct AudioDeviceInfo { - char name[128]; - S32 id; // index into engine's device list + char name[128]; + S32 id; // index into engine's device list }; -AudioEngine *audio_create(void *hwnd); -void audio_destroy(AudioEngine *engine); -void audio_refresh_devices(AudioEngine *engine); -S32 audio_get_device_count(AudioEngine *engine); -AudioDeviceInfo*audio_get_device(AudioEngine *engine, S32 index); +AudioEngine *audio_create(void *hwnd); +void audio_destroy(AudioEngine *engine); +void audio_refresh_devices(AudioEngine *engine); +S32 audio_get_device_count(AudioEngine *engine); +AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index); -B32 audio_open_device(AudioEngine *engine, S32 index); -void audio_close_device(AudioEngine *engine); +B32 audio_open_device(AudioEngine *engine, S32 index); +void audio_close_device(AudioEngine *engine); -void audio_play_test_tone(AudioEngine *engine); -B32 audio_is_test_tone_playing(AudioEngine *engine); -void audio_update(AudioEngine *engine, F32 dt); +void audio_play_test_tone(AudioEngine *engine); +B32 audio_is_test_tone_playing(AudioEngine *engine); +void audio_update(AudioEngine *engine, F32 dt); diff --git a/src/audio/audio_asio.cpp b/src/audio/audio_asio.cpp index b02dd45..5af63f6 100644 --- a/src/audio/audio_asio.cpp +++ b/src/audio/audio_asio.cpp @@ -1,8 +1,8 @@ #include "audio/audio.h" -#include +#include #include #include -#include +#include #define AUDIO_MAX_DEVICES 32 #define AUDIO_MAX_CHANNELS 32 @@ -13,21 +13,21 @@ //////////////////////////////// // ASIO type definitions (minimal set for a host) -typedef long ASIOError; -typedef long ASIOBool; +typedef long ASIOError; +typedef long ASIOBool; typedef long long int ASIOSamples; typedef long long int ASIOTimeStamp; enum { - ASE_OK = 0, - ASE_SUCCESS = 0x3f4847a0, - ASE_NotPresent = -1000, - ASE_HWMalfunction = -999, + ASE_OK = 0, + ASE_SUCCESS = 0x3f4847a0, + ASE_NotPresent = -1000, + ASE_HWMalfunction = -999, ASE_InvalidParameter = -998, - ASE_InvalidMode = -997, - ASE_SPNotAdvancing = -996, - ASE_NoClock = -995, - ASE_NoMemory = -994, + ASE_InvalidMode = -997, + ASE_SPNotAdvancing = -996, + ASE_NoClock = -995, + ASE_NoMemory = -994, }; enum ASIOSampleType { @@ -55,20 +55,20 @@ enum ASIOSampleType { }; struct ASIOClockSource { - long index; - long channel; - long group; + long index; + long channel; + long group; ASIOBool isCurrentSource; - char name[32]; + char name[32]; }; struct ASIOChannelInfo { - long channel; - ASIOBool isInput; - ASIOBool isActive; - long channelGroup; + long channel; + ASIOBool isInput; + ASIOBool isActive; + long channelGroup; ASIOSampleType type; - char name[32]; + char name[32]; }; struct ASIOBufferInfo { @@ -78,31 +78,31 @@ struct ASIOBufferInfo { }; struct ASIOTimeCode { - F64 speed; - ASIOSamples timeCodeSamples; - unsigned long flags; - char future[64]; + F64 speed; + ASIOSamples timeCodeSamples; + unsigned long flags; + char future[64]; }; struct AsioTimeInfo { - F64 speed; - ASIOTimeStamp systemTime; - ASIOSamples samplePosition; - F64 sampleRate; - unsigned long flags; - char reserved[12]; + F64 speed; + ASIOTimeStamp systemTime; + ASIOSamples samplePosition; + F64 sampleRate; + unsigned long flags; + char reserved[12]; }; struct ASIOTime { - long reserved[4]; - AsioTimeInfo timeInfo; - ASIOTimeCode timeCode; + long reserved[4]; + AsioTimeInfo timeInfo; + ASIOTimeCode timeCode; }; struct ASIOCallbacks { - void (*bufferSwitch)(long doubleBufferIndex, ASIOBool directProcess); - void (*sampleRateDidChange)(F64 sRate); - long (*asioMessage)(long selector, long value, void *message, F64 *opt); + void (*bufferSwitch)(long doubleBufferIndex, ASIOBool directProcess); + void (*sampleRateDidChange)(F64 sRate); + long (*asioMessage)(long selector, long value, void *message, F64 *opt); ASIOTime *(*bufferSwitchTimeInfo)(ASIOTime *params, long doubleBufferIndex, ASIOBool directProcess); }; @@ -124,35 +124,35 @@ enum { // Standard ASIO vtable — inherits IUnknown class IASIO : public IUnknown { -public: - virtual ASIOBool init(void *sysHandle) = 0; - virtual void getDriverName(char *name) = 0; - virtual long getDriverVersion() = 0; - virtual void getErrorMessage(char *string) = 0; - virtual ASIOError start() = 0; - virtual ASIOError stop() = 0; - virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; - virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; - virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0; - virtual ASIOError canSampleRate(F64 sampleRate) = 0; - virtual ASIOError getSampleRate(F64 *sampleRate) = 0; - virtual ASIOError setSampleRate(F64 sampleRate) = 0; - virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; - virtual ASIOError setClockSource(long reference) = 0; - virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; - virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; - virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) = 0; - virtual ASIOError disposeBuffers() = 0; - virtual ASIOError controlPanel() = 0; - virtual ASIOError future(long selector, void *opt) = 0; - virtual ASIOError outputReady() = 0; + public: + virtual ASIOBool init(void *sysHandle) = 0; + virtual void getDriverName(char *name) = 0; + virtual long getDriverVersion() = 0; + virtual void getErrorMessage(char *string) = 0; + virtual ASIOError start() = 0; + virtual ASIOError stop() = 0; + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; + virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0; + virtual ASIOError canSampleRate(F64 sampleRate) = 0; + virtual ASIOError getSampleRate(F64 *sampleRate) = 0; + virtual ASIOError setSampleRate(F64 sampleRate) = 0; + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; + virtual ASIOError setClockSource(long reference) = 0; + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; + virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) = 0; + virtual ASIOError disposeBuffers() = 0; + virtual ASIOError controlPanel() = 0; + virtual ASIOError future(long selector, void *opt) = 0; + virtual ASIOError outputReady() = 0; }; //////////////////////////////// // Internal state struct AsioDriverInfo { - char name[128]; + char name[128]; CLSID clsid; }; @@ -162,25 +162,25 @@ struct AudioEngine { // Device enumeration AudioDeviceInfo devices[AUDIO_MAX_DEVICES]; AsioDriverInfo drivers[AUDIO_MAX_DEVICES]; - S32 device_count; + S32 device_count; // Active driver - IASIO *driver; - S32 active_device_index; // -1 = none + IASIO *driver; + S32 active_device_index; // -1 = none // Buffer state - ASIOBufferInfo buffer_infos[AUDIO_MAX_CHANNELS]; - long num_output_channels; - long num_input_channels; - long buffer_size; - F64 sample_rate; - ASIOSampleType output_sample_type; - ASIOCallbacks callbacks; + ASIOBufferInfo buffer_infos[AUDIO_MAX_CHANNELS]; + long num_output_channels; + long num_input_channels; + long buffer_size; + F64 sample_rate; + ASIOSampleType output_sample_type; + ASIOCallbacks callbacks; // Test tone state (accessed from callback thread) - volatile LONG test_tone_active; - volatile LONG test_tone_samples_remaining; - F64 test_tone_phase; // written only from callback thread + volatile LONG test_tone_active; + volatile LONG test_tone_samples_remaining; + F64 test_tone_phase; // written only from callback thread }; //////////////////////////////// @@ -204,9 +204,9 @@ static void write_sample(void *dest, ASIOSampleType type, F64 value) { case ASIOSTInt24LSB: { S32 s = (S32)(value * 8388607.0); U8 *d = (U8 *)dest; - d[0] = (U8)(s & 0xFF); - d[1] = (U8)((s >> 8) & 0xFF); - d[2] = (U8)((s >> 16) & 0xFF); + d[0] = (U8)(s & 0xFF); + d[1] = (U8)((s >> 8) & 0xFF); + d[2] = (U8)((s >> 16) & 0xFF); } break; case ASIOSTInt32LSB: { S32 s = (S32)(value * 2147483647.0); @@ -229,12 +229,12 @@ static void write_sample(void *dest, ASIOSampleType type, F64 value) { static int sample_type_size(ASIOSampleType type) { switch (type) { - case ASIOSTInt16LSB: return 2; - case ASIOSTInt24LSB: return 3; - case ASIOSTInt32LSB: return 4; + case ASIOSTInt16LSB: return 2; + case ASIOSTInt24LSB: return 3; + case ASIOSTInt32LSB: return 4; case ASIOSTFloat32LSB: return 4; case ASIOSTFloat64LSB: return 8; - default: return 4; + default: return 4; } } @@ -247,9 +247,9 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) { AudioEngine *engine = g_audio_engine; if (!engine) return; - long buf_size = engine->buffer_size; - ASIOSampleType type = engine->output_sample_type; - S32 bytes_per_sample = sample_type_size(type); + long buf_size = engine->buffer_size; + ASIOSampleType type = engine->output_sample_type; + S32 bytes_per_sample = sample_type_size(type); LONG tone_active = InterlockedCompareExchange(&engine->test_tone_active, 0, 0); @@ -266,9 +266,9 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) { } if (tone_active) { - F64 phase = engine->test_tone_phase; - F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate; - LONG remaining = InterlockedCompareExchange(&engine->test_tone_samples_remaining, 0, 0); + F64 phase = engine->test_tone_phase; + F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate; + LONG remaining = InterlockedCompareExchange(&engine->test_tone_samples_remaining, 0, 0); long samples_to_gen = (remaining < buf_size) ? (long)remaining : buf_size; for (long s = 0; s < buf_size; s++) { @@ -346,7 +346,7 @@ static void enumerate_asio_drivers(AudioEngine *engine) { if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO", 0, KEY_READ, &asio_key) != ERROR_SUCCESS) return; - char subkey_name[256]; + char subkey_name[256]; DWORD subkey_name_len; for (DWORD i = 0; engine->device_count < AUDIO_MAX_DEVICES; i++) { @@ -359,9 +359,9 @@ static void enumerate_asio_drivers(AudioEngine *engine) { continue; // Read CLSID string - char clsid_str[64] = {}; - DWORD clsid_len = sizeof(clsid_str); - DWORD type = 0; + char clsid_str[64] = {}; + DWORD clsid_len = sizeof(clsid_str); + DWORD type = 0; if (RegQueryValueExA(driver_key, "CLSID", nullptr, &type, (LPBYTE)clsid_str, &clsid_len) != ERROR_SUCCESS || type != REG_SZ) { RegCloseKey(driver_key); @@ -369,7 +369,7 @@ static void enumerate_asio_drivers(AudioEngine *engine) { } // Parse CLSID string to GUID - CLSID clsid; + CLSID clsid; wchar_t clsid_wide[64]; MultiByteToWideChar(CP_ACP, 0, clsid_str, -1, clsid_wide, 64); if (CLSIDFromString(clsid_wide, &clsid) != S_OK) { @@ -379,7 +379,7 @@ static void enumerate_asio_drivers(AudioEngine *engine) { S32 idx = engine->device_count++; strncpy_s(engine->devices[idx].name, sizeof(engine->devices[idx].name), subkey_name, _TRUNCATE); - engine->devices[idx].id = idx; + engine->devices[idx].id = idx; engine->drivers[idx].clsid = clsid; strncpy_s(engine->drivers[idx].name, sizeof(engine->drivers[idx].name), subkey_name, _TRUNCATE); @@ -395,9 +395,9 @@ static void enumerate_asio_drivers(AudioEngine *engine) { AudioEngine *audio_create(void *hwnd) { AudioEngine *engine = new AudioEngine(); memset(engine, 0, sizeof(*engine)); - engine->hwnd = hwnd; + engine->hwnd = hwnd; engine->active_device_index = -1; - g_audio_engine = engine; + g_audio_engine = engine; CoInitialize(nullptr); enumerate_asio_drivers(engine); @@ -433,11 +433,11 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { return false; // Create COM instance of the ASIO driver - IASIO *driver = nullptr; - HRESULT hr = CoCreateInstance(engine->drivers[index].clsid, - nullptr, CLSCTX_INPROC_SERVER, - engine->drivers[index].clsid, - (void **)&driver); + IASIO *driver = nullptr; + HRESULT hr = CoCreateInstance(engine->drivers[index].clsid, + nullptr, CLSCTX_INPROC_SERVER, + engine->drivers[index].clsid, + (void **)&driver); if (FAILED(hr) || !driver) return false; @@ -475,8 +475,8 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { // Query output channel sample type ASIOChannelInfo chan_info = {}; - chan_info.channel = 0; - chan_info.isInput = 0; + chan_info.channel = 0; + chan_info.isInput = 0; if (driver->getChannelInfo(&chan_info) != ASE_OK) { driver->Release(); return false; @@ -488,16 +488,16 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { // Setup buffer infos for output channels only memset(engine->buffer_infos, 0, sizeof(engine->buffer_infos)); for (long ch = 0; ch < num_out; ch++) { - engine->buffer_infos[ch].isInput = 0; + engine->buffer_infos[ch].isInput = 0; engine->buffer_infos[ch].channelNum = ch; engine->buffer_infos[ch].buffers[0] = nullptr; engine->buffer_infos[ch].buffers[1] = nullptr; } // Setup callbacks - engine->callbacks.bufferSwitch = asio_buffer_switch; - engine->callbacks.sampleRateDidChange = asio_sample_rate_changed; - engine->callbacks.asioMessage = asio_message; + engine->callbacks.bufferSwitch = asio_buffer_switch; + engine->callbacks.sampleRateDidChange = asio_sample_rate_changed; + engine->callbacks.asioMessage = asio_message; engine->callbacks.bufferSwitchTimeInfo = asio_buffer_switch_time_info; // Create buffers @@ -507,22 +507,22 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { } // Store state - engine->driver = driver; - engine->active_device_index = index; - engine->num_output_channels = num_out; - engine->num_input_channels = num_in; - engine->buffer_size = preferred_size; - engine->sample_rate = sample_rate; - engine->output_sample_type = chan_info.type; - engine->test_tone_active = 0; + engine->driver = driver; + engine->active_device_index = index; + engine->num_output_channels = num_out; + engine->num_input_channels = num_in; + engine->buffer_size = preferred_size; + engine->sample_rate = sample_rate; + engine->output_sample_type = chan_info.type; + engine->test_tone_active = 0; engine->test_tone_samples_remaining = 0; - engine->test_tone_phase = 0.0; + engine->test_tone_phase = 0.0; // Start the driver if (driver->start() != ASE_OK) { driver->disposeBuffers(); driver->Release(); - engine->driver = nullptr; + engine->driver = nullptr; engine->active_device_index = -1; return false; } @@ -542,16 +542,16 @@ void audio_close_device(AudioEngine *engine) { engine->driver->stop(); engine->driver->disposeBuffers(); engine->driver->Release(); - engine->driver = nullptr; + engine->driver = nullptr; engine->active_device_index = -1; - engine->test_tone_phase = 0.0; + engine->test_tone_phase = 0.0; } void audio_play_test_tone(AudioEngine *engine) { if (!engine->driver) return; engine->test_tone_phase = 0.0; - LONG total_samples = (LONG)(engine->sample_rate * AUDIO_TEST_TONE_SEC); + LONG total_samples = (LONG)(engine->sample_rate * AUDIO_TEST_TONE_SEC); InterlockedExchange(&engine->test_tone_samples_remaining, total_samples); InterlockedExchange(&engine->test_tone_active, 1); } diff --git a/src/audio/audio_coreaudio.cpp b/src/audio/audio_coreaudio.cpp index 215bcc3..ac48716 100644 --- a/src/audio/audio_coreaudio.cpp +++ b/src/audio/audio_coreaudio.cpp @@ -1,9 +1,9 @@ #include "audio/audio.h" #include #include -#include #include #include +#include #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -18,20 +18,20 @@ struct CoreAudioDeviceInfo { }; struct AudioEngine { - AudioDeviceInfo devices[AUDIO_MAX_DEVICES]; - CoreAudioDeviceInfo ca_devices[AUDIO_MAX_DEVICES]; - S32 device_count; + AudioDeviceInfo devices[AUDIO_MAX_DEVICES]; + CoreAudioDeviceInfo ca_devices[AUDIO_MAX_DEVICES]; + S32 device_count; - AUGraph graph; - AudioUnit output_unit; - S32 active_device_index; - F64 sample_rate; - S32 num_channels; + AUGraph graph; + AudioUnit output_unit; + S32 active_device_index; + F64 sample_rate; + S32 num_channels; // Test tone state (accessed from audio render thread) - _Atomic S32 test_tone_active; - _Atomic S32 test_tone_samples_remaining; - F64 test_tone_phase; + _Atomic S32 test_tone_active; + _Atomic S32 test_tone_samples_remaining; + F64 test_tone_phase; }; //////////////////////////////// @@ -39,14 +39,16 @@ struct AudioEngine { static AudioEngine *g_audio_engine = nullptr; -static OSStatus audio_render_callback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) -{ - (void)inRefCon; (void)ioActionFlags; (void)inTimeStamp; (void)inBusNumber; +static OSStatus audio_render_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) { + (void)inRefCon; + (void)ioActionFlags; + (void)inTimeStamp; + (void)inBusNumber; AudioEngine *engine = g_audio_engine; if (!engine) { @@ -63,15 +65,15 @@ static OSStatus audio_render_callback(void *inRefCon, return noErr; } - F64 phase = engine->test_tone_phase; - F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate; - S32 remaining = atomic_load(&engine->test_tone_samples_remaining); + F64 phase = engine->test_tone_phase; + F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate; + S32 remaining = atomic_load(&engine->test_tone_samples_remaining); S32 samples_to_gen = (remaining < (S32)inNumberFrames) ? remaining : (S32)inNumberFrames; // CoreAudio with kAudioFormatFlagIsNonInterleaved: each buffer = one channel for (UInt32 buf = 0; buf < ioData->mNumberBuffers; buf++) { F32 *out = (F32 *)ioData->mBuffers[buf].mData; - F64 p = phase; + F64 p = phase; for (UInt32 s = 0; s < inNumberFrames; s++) { if ((S32)s < samples_to_gen) { out[s] = (F32)(sin(p) * 0.5); // -6dB @@ -85,7 +87,8 @@ static OSStatus audio_render_callback(void *inRefCon, // Advance phase using first channel's traversal phase += phase_inc * samples_to_gen; - while (phase >= 2.0 * AUDIO_PI) phase -= 2.0 * AUDIO_PI; + while (phase >= 2.0 * AUDIO_PI) + phase -= 2.0 * AUDIO_PI; engine->test_tone_phase = phase; S32 new_remaining = atomic_fetch_sub(&engine->test_tone_samples_remaining, samples_to_gen); @@ -154,8 +157,8 @@ static void enumerate_output_devices(AudioEngine *engine) { kAudioObjectPropertyElementMain }; - CFStringRef name_ref = nullptr; - UInt32 name_size = sizeof(name_ref); + CFStringRef name_ref = nullptr; + UInt32 name_size = sizeof(name_ref); if (AudioObjectGetPropertyData(device_ids[i], &name_prop, 0, nullptr, &name_size, &name_ref) != noErr) continue; @@ -163,7 +166,7 @@ static void enumerate_output_devices(AudioEngine *engine) { CFStringGetCString(name_ref, engine->devices[idx].name, sizeof(engine->devices[idx].name), kCFStringEncodingUTF8); CFRelease(name_ref); - engine->devices[idx].id = idx; + engine->devices[idx].id = idx; engine->ca_devices[idx].device_id = device_ids[i]; } @@ -219,9 +222,9 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { // Add HAL output node AudioComponentDescription output_desc = {}; - output_desc.componentType = kAudioUnitType_Output; - output_desc.componentSubType = kAudioUnitSubType_HALOutput; - output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; + output_desc.componentType = kAudioUnitType_Output; + output_desc.componentSubType = kAudioUnitSubType_HALOutput; + output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; AUNode output_node; if (AUGraphAddNode(engine->graph, &output_desc, &output_node) != noErr) { @@ -244,7 +247,7 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { // Set device if (AudioUnitSetProperty(engine->output_unit, kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, 0, &device_id, sizeof(device_id)) != noErr) { + kAudioUnitScope_Global, 0, &device_id, sizeof(device_id)) != noErr) { DisposeAUGraph(engine->graph); engine->graph = nullptr; return false; @@ -257,32 +260,32 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { kAudioObjectPropertyElementMain }; Float64 sample_rate = 44100.0; - UInt32 rate_size = sizeof(sample_rate); + UInt32 rate_size = sizeof(sample_rate); AudioObjectGetPropertyData(device_id, &rate_prop, 0, nullptr, &rate_size, &sample_rate); engine->sample_rate = sample_rate; // Set stream format: Float32, non-interleaved AudioStreamBasicDescription fmt = {}; - fmt.mSampleRate = sample_rate; - fmt.mFormatID = kAudioFormatLinearPCM; - fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked; - fmt.mBitsPerChannel = 32; - fmt.mChannelsPerFrame = 2; - fmt.mFramesPerPacket = 1; - fmt.mBytesPerFrame = 4; - fmt.mBytesPerPacket = 4; - engine->num_channels = 2; + fmt.mSampleRate = sample_rate; + fmt.mFormatID = kAudioFormatLinearPCM; + fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked; + fmt.mBitsPerChannel = 32; + fmt.mChannelsPerFrame = 2; + fmt.mFramesPerPacket = 1; + fmt.mBytesPerFrame = 4; + fmt.mBytesPerPacket = 4; + engine->num_channels = 2; AudioUnitSetProperty(engine->output_unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &fmt, sizeof(fmt)); + kAudioUnitScope_Input, 0, &fmt, sizeof(fmt)); // Set render callback AURenderCallbackStruct cb = {}; - cb.inputProc = audio_render_callback; - cb.inputProcRefCon = engine; + cb.inputProc = audio_render_callback; + cb.inputProcRefCon = engine; if (AudioUnitSetProperty(engine->output_unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &cb, sizeof(cb)) != noErr) { + kAudioUnitScope_Input, 0, &cb, sizeof(cb)) != noErr) { DisposeAUGraph(engine->graph); engine->graph = nullptr; return false; @@ -302,7 +305,7 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { } engine->active_device_index = index; - engine->test_tone_phase = 0.0; + engine->test_tone_phase = 0.0; atomic_store(&engine->test_tone_active, 0); atomic_store(&engine->test_tone_samples_remaining, 0); @@ -318,17 +321,17 @@ void audio_close_device(AudioEngine *engine) { AUGraphStop(engine->graph); AUGraphUninitialize(engine->graph); DisposeAUGraph(engine->graph); - engine->graph = nullptr; - engine->output_unit = nullptr; + engine->graph = nullptr; + engine->output_unit = nullptr; engine->active_device_index = -1; - engine->test_tone_phase = 0.0; + engine->test_tone_phase = 0.0; } void audio_play_test_tone(AudioEngine *engine) { if (!engine->graph) return; engine->test_tone_phase = 0.0; - S32 total_samples = (S32)(engine->sample_rate * AUDIO_TEST_TONE_SEC); + S32 total_samples = (S32)(engine->sample_rate * AUDIO_TEST_TONE_SEC); atomic_store(&engine->test_tone_samples_remaining, total_samples); atomic_store(&engine->test_tone_active, 1); } diff --git a/src/base/base_arena.cpp b/src/base/base_arena.cpp index ce665f7..4359474 100644 --- a/src/base/base_arena.cpp +++ b/src/base/base_arena.cpp @@ -5,9 +5,9 @@ Arena *arena_alloc(U64 cap) { U8 *mem = (U8 *)malloc(sizeof(Arena) + cap); if (!mem) return nullptr; Arena *arena = (Arena *)mem; - arena->base = mem + sizeof(Arena); - arena->pos = 0; - arena->cap = cap; + arena->base = mem + sizeof(Arena); + arena->pos = 0; + arena->cap = cap; return arena; } diff --git a/src/base/base_arena.h b/src/base/base_arena.h index 6b16c99..db79618 100644 --- a/src/base/base_arena.h +++ b/src/base/base_arena.h @@ -9,9 +9,9 @@ // Arena type struct Arena { - U8 *base; - U64 pos; - U64 cap; + U8 *base; + U64 pos; + U64 cap; }; // Temporary scope (save/restore position) @@ -34,8 +34,8 @@ void arena_clear(Arena *arena); //////////////////////////////// // Temporary scope helpers -inline Temp temp_begin(Arena *arena) { return {arena, arena->pos}; } -inline void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.pos); } +inline Temp temp_begin(Arena *arena) { return { arena, arena->pos }; } +inline void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.pos); } //////////////////////////////// // Push helper macros diff --git a/src/base/base_core.h b/src/base/base_core.h index 50b5553..0fc6efa 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -2,18 +2,18 @@ // base_core.h - Fundamental types, macros, and linked list helpers // Inspired by raddebugger's base_core.h -#include -#include -#include -#include #include +#include +#include +#include +#include //////////////////////////////// // Codebase keywords #ifndef __APPLE__ -#define internal static -#define global static +#define internal static +#define global static #endif #define local_persist static @@ -60,25 +60,26 @@ typedef double F64; #define Max(A, B) (((A) > (B)) ? (A) : (B)) #define ClampTop(A, X) Min(A, X) #define ClampBot(X, B) Max(X, B) -#define Clamp(A, X, B) (((X) < (A)) ? (A) : ((X) > (B)) ? (B) : (X)) +#define Clamp(A, X, B) (((X) < (A)) ? (A) : ((X) > (B)) ? (B) \ + : (X)) //////////////////////////////// // Alignment / Sizing -#define AlignPow2(x, b) (((x) + (b) - 1) & (~((b) - 1))) -#define AlignDownPow2(x, b) ((x) & (~((b) - 1))) -#define ArrayCount(a) (sizeof(a) / sizeof((a)[0])) +#define AlignPow2(x, b) (((x) + (b)-1) & (~((b)-1))) +#define AlignDownPow2(x, b) ((x) & (~((b)-1))) +#define ArrayCount(a) (sizeof(a) / sizeof((a)[0])) //////////////////////////////// // Memory macros -#define MemoryCopy(dst, src, size) memmove((dst), (src), (size)) -#define MemorySet(dst, byte, size) memset((dst), (byte), (size)) -#define MemoryCompare(a, b, size) memcmp((a), (b), (size)) -#define MemoryZero(s, z) memset((s), 0, (z)) -#define MemoryZeroStruct(s) MemoryZero((s), sizeof(*(s))) -#define MemoryZeroArray(a) MemoryZero((a), sizeof(a)) -#define MemoryMatch(a, b, z) (MemoryCompare((a), (b), (z)) == 0) +#define MemoryCopy(dst, src, size) memmove((dst), (src), (size)) +#define MemorySet(dst, byte, size) memset((dst), (byte), (size)) +#define MemoryCompare(a, b, size) memcmp((a), (b), (size)) +#define MemoryZero(s, z) memset((s), 0, (z)) +#define MemoryZeroStruct(s) MemoryZero((s), sizeof(*(s))) +#define MemoryZeroArray(a) MemoryZero((a), sizeof(a)) +#define MemoryMatch(a, b, z) (MemoryCompare((a), (b), (z)) == 0) //////////////////////////////// // Pointer / integer casts @@ -109,25 +110,33 @@ typedef double F64; #define Glue_(A, B) A##B #define Glue(A, B) Glue_(A, B) -#define Swap(T, a, b) do { T t__ = a; a = b; b = t__; } while (0) +#define Swap(T, a, b) \ + do { \ + T t__ = a; \ + a = b; \ + b = t__; \ + } while (0) //////////////////////////////// // Assert #if defined(_MSC_VER) -# define Trap() __debugbreak() +#define Trap() __debugbreak() #elif defined(__clang__) || defined(__GNUC__) -# define Trap() __builtin_trap() +#define Trap() __builtin_trap() #else -# define Trap() (*(volatile int *)0 = 0) +#define Trap() (*(volatile int *)0 = 0) #endif -#define AssertAlways(x) do { if (!(x)) { Trap(); } } while (0) +#define AssertAlways(x) \ + do { \ + if (!(x)) { Trap(); } \ + } while (0) #ifdef _DEBUG -# define Assert(x) AssertAlways(x) +#define Assert(x) AssertAlways(x) #else -# define Assert(x) (void)(x) +#define Assert(x) (void)(x) #endif #define InvalidPath Assert(!"Invalid Path!") @@ -141,22 +150,17 @@ typedef double F64; #define SetNil(nil, p) ((p) = nil) // Doubly-linked-list (with nil support) -#define DLLInsert_NPZ(nil, f, l, p, n, next, prev) \ - (CheckNil(nil, f) ? \ - ((f) = (l) = (n), SetNil(nil, (n)->next), SetNil(nil, (n)->prev)) : \ - CheckNil(nil, p) ? \ - ((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil, (n)->prev)) : \ - ((p) == (l)) ? \ - ((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) : \ - (((!CheckNil(nil, p) && CheckNil(nil, (p)->next)) ? (0) : ((p)->next->prev = (n))), \ - ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p)))) +#define DLLInsert_NPZ(nil, f, l, p, n, next, prev) \ + (CheckNil(nil, f) ? ((f) = (l) = (n), SetNil(nil, (n)->next), SetNil(nil, (n)->prev)) : CheckNil(nil, p) ? ((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil, (n)->prev)) \ + : ((p) == (l)) ? ((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) \ + : (((!CheckNil(nil, p) && CheckNil(nil, (p)->next)) ? (0) : ((p)->next->prev = (n))), ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p)))) #define DLLPushBack_NPZ(nil, f, l, n, next, prev) DLLInsert_NPZ(nil, f, l, l, n, next, prev) #define DLLPushFront_NPZ(nil, f, l, n, next, prev) DLLInsert_NPZ(nil, l, f, f, n, prev, next) -#define DLLRemove_NPZ(nil, f, l, n, next, prev) \ - (((n) == (f) ? (f) = (n)->next : (0)), \ - ((n) == (l) ? (l) = (l)->prev : (0)), \ +#define DLLRemove_NPZ(nil, f, l, n, next, prev) \ + (((n) == (f) ? (f) = (n)->next : (0)), \ + ((n) == (l) ? (l) = (l)->prev : (0)), \ (CheckNil(nil, (n)->prev) ? (0) : ((n)->prev->next = (n)->next)), \ (CheckNil(nil, (n)->next) ? (0) : ((n)->next->prev = (n)->prev))) @@ -167,9 +171,7 @@ typedef double F64; // Singly-linked queue (doubly-headed) #define SLLQueuePush_NZ(nil, f, l, n, next) \ - (CheckNil(nil, f) ? \ - ((f) = (l) = (n), SetNil(nil, (n)->next)) : \ - ((l)->next = (n), (l) = (n), SetNil(nil, (n)->next))) + (CheckNil(nil, f) ? ((f) = (l) = (n), SetNil(nil, (n)->next)) : ((l)->next = (n), (l) = (n), SetNil(nil, (n)->next))) #define SLLQueuePush(f, l, n) SLLQueuePush_NZ(0, f, l, n, next) #define SLLQueuePushFront(f, l, n) (((n)->next = (f)), ((f) = (n))) diff --git a/src/base/base_inc.h b/src/base/base_inc.h index 8c0855d..51c5648 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -2,7 +2,7 @@ // base_inc.h - Umbrella include for the base layer // Include this one header to get all base types. -#include "base/base_core.h" #include "base/base_arena.h" +#include "base/base_core.h" #include "base/base_math.h" #include "base/base_strings.h" diff --git a/src/base/base_math.h b/src/base/base_math.h index 9886dae..e8407da 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -30,72 +30,87 @@ enum Corner { //////////////////////////////// // Vector types -struct Vec2F32 { F32 x, y; }; -struct Vec2S32 { S32 x, y; }; -struct Vec3F32 { F32 x, y, z; }; -struct Vec4F32 { F32 x, y, z, w; }; +struct Vec2F32 { + F32 x, y; +}; +struct Vec2S32 { + S32 x, y; +}; +struct Vec3F32 { + F32 x, y, z; +}; +struct Vec4F32 { + F32 x, y, z, w; +}; //////////////////////////////// // Range types -struct Rng1F32 { F32 min, max; }; -struct Rng1S64 { S64 min, max; }; -struct Rng2F32 { Vec2F32 p0, p1; }; +struct Rng1F32 { + F32 min, max; +}; +struct Rng1S64 { + S64 min, max; +}; +struct Rng2F32 { + Vec2F32 p0, p1; +}; //////////////////////////////// // Constructors -static inline Vec2F32 v2f32(F32 x, F32 y) { return {x, y}; } -static inline Vec2S32 v2s32(S32 x, S32 y) { return {x, y}; } -static inline Vec3F32 v3f32(F32 x, F32 y, F32 z) { return {x, y, z}; } -static inline Vec4F32 v4f32(F32 x, F32 y, F32 z, F32 w) { return {x, y, z, w}; } -static inline Rng1F32 rng1f32(F32 min, F32 max) { return {min, max}; } -static inline Rng1S64 rng1s64(S64 min, S64 max) { return {min, max}; } -static inline Rng2F32 rng2f32(Vec2F32 p0, Vec2F32 p1) { return {p0, p1}; } -static inline Rng2F32 rng2f32p(F32 x0, F32 y0, F32 x1, F32 y1) { return {{x0, y0}, {x1, y1}}; } +static inline Vec2F32 v2f32(F32 x, F32 y) { return { x, y }; } +static inline Vec2S32 v2s32(S32 x, S32 y) { return { x, y }; } +static inline Vec3F32 v3f32(F32 x, F32 y, F32 z) { return { x, y, z }; } +static inline Vec4F32 v4f32(F32 x, F32 y, F32 z, F32 w) { return { x, y, z, w }; } +static inline Rng1F32 rng1f32(F32 min, F32 max) { return { min, max }; } +static inline Rng1S64 rng1s64(S64 min, S64 max) { return { min, max }; } +static inline Rng2F32 rng2f32(Vec2F32 p0, Vec2F32 p1) { return { p0, p1 }; } +static inline Rng2F32 rng2f32p(F32 x0, F32 y0, F32 x1, F32 y1) { return { { x0, y0 }, { x1, y1 } }; } //////////////////////////////// // Vec2F32 operations -static inline Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) { return {a.x + b.x, a.y + b.y}; } -static inline Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) { return {a.x - b.x, a.y - b.y}; } -static inline Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) { return {a.x * b.x, a.y * b.y}; } -static inline Vec2F32 scale_2f32(Vec2F32 v, F32 s) { return {v.x * s, v.y * s}; } +static inline Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) { return { a.x + b.x, a.y + b.y }; } +static inline Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) { return { a.x - b.x, a.y - b.y }; } +static inline Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) { return { a.x * b.x, a.y * b.y }; } +static inline Vec2F32 scale_2f32(Vec2F32 v, F32 s) { return { v.x * s, v.y * s }; } // Axis-indexed access -static inline F32 v2f32_axis(Vec2F32 v, Axis2 a) { return a == Axis2_X ? v.x : v.y; } +static inline F32 v2f32_axis(Vec2F32 v, Axis2 a) { return a == Axis2_X ? v.x : v.y; } static inline void v2f32_set_axis(Vec2F32 *v, Axis2 a, F32 val) { - if (a == Axis2_X) v->x = val; else v->y = val; + if (a == Axis2_X) v->x = val; + else v->y = val; } //////////////////////////////// // Vec4F32 operations -static inline Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) { return {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; } -static inline Vec4F32 scale_4f32(Vec4F32 v, F32 s) { return {v.x*s, v.y*s, v.z*s, v.w*s}; } +static inline Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +static inline Vec4F32 scale_4f32(Vec4F32 v, F32 s) { return { v.x * s, v.y * s, v.z * s, v.w * s }; } static inline Vec4F32 lerp_4f32(Vec4F32 a, Vec4F32 b, F32 t) { - return {a.x + (b.x - a.x)*t, a.y + (b.y - a.y)*t, a.z + (b.z - a.z)*t, a.w + (b.w - a.w)*t}; + return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t }; } //////////////////////////////// // Rng2F32 operations -static inline F32 rng2f32_width(Rng2F32 r) { return r.p1.x - r.p0.x; } -static inline F32 rng2f32_height(Rng2F32 r) { return r.p1.y - r.p0.y; } -static inline Vec2F32 rng2f32_dim(Rng2F32 r) { return {r.p1.x - r.p0.x, r.p1.y - r.p0.y}; } -static inline Vec2F32 rng2f32_center(Rng2F32 r) { return {(r.p0.x + r.p1.x)*0.5f, (r.p0.y + r.p1.y)*0.5f}; } +static inline F32 rng2f32_width(Rng2F32 r) { return r.p1.x - r.p0.x; } +static inline F32 rng2f32_height(Rng2F32 r) { return r.p1.y - r.p0.y; } +static inline Vec2F32 rng2f32_dim(Rng2F32 r) { return { r.p1.x - r.p0.x, r.p1.y - r.p0.y }; } +static inline Vec2F32 rng2f32_center(Rng2F32 r) { return { (r.p0.x + r.p1.x) * 0.5f, (r.p0.y + r.p1.y) * 0.5f }; } static inline B32 rng2f32_contains(Rng2F32 r, Vec2F32 p) { return p.x >= r.p0.x && p.x <= r.p1.x && p.y >= r.p0.y && p.y <= r.p1.y; } static inline Rng2F32 rng2f32_pad(Rng2F32 r, F32 p) { - return {{r.p0.x - p, r.p0.y - p}, {r.p1.x + p, r.p1.y + p}}; + return { { r.p0.x - p, r.p0.y - p }, { r.p1.x + p, r.p1.y + p } }; } static inline Rng2F32 rng2f32_shift(Rng2F32 r, Vec2F32 v) { - return {{r.p0.x + v.x, r.p0.y + v.y}, {r.p1.x + v.x, r.p1.y + v.y}}; + return { { r.p0.x + v.x, r.p0.y + v.y }, { r.p1.x + v.x, r.p1.y + v.y } }; } static inline Rng2F32 rng2f32_intersect(Rng2F32 a, Rng2F32 b) { - return {{Max(a.p0.x, b.p0.x), Max(a.p0.y, b.p0.y)}, - {Min(a.p1.x, b.p1.x), Min(a.p1.y, b.p1.y)}}; + return { { Max(a.p0.x, b.p0.x), Max(a.p0.y, b.p0.y) }, + { Min(a.p1.x, b.p1.x), Min(a.p1.y, b.p1.y) } }; } // Axis-indexed range dimension @@ -107,4 +122,4 @@ static inline F32 rng2f32_dim_axis(Rng2F32 r, Axis2 a) { // F32 helpers static inline F32 lerp_1f32(F32 a, F32 b, F32 t) { return a + (b - a) * t; } -static inline F32 abs_f32(F32 x) { return x < 0 ? -x : x; } +static inline F32 abs_f32(F32 x) { return x < 0 ? -x : x; } diff --git a/src/base/base_strings.cpp b/src/base/base_strings.cpp index 87eebcb..7c9be7a 100644 --- a/src/base/base_strings.cpp +++ b/src/base/base_strings.cpp @@ -12,20 +12,20 @@ Str8 str8_pushf(Arena *arena, const char *fmt, ...) { vsnprintf(buf, len + 1, fmt, args2); va_end(args2); - return {buf, (U64)len}; + return { buf, (U64)len }; } Str8 str8_push_copy(Arena *arena, Str8 s) { - if (s.size == 0 || !s.str) return {nullptr, 0}; + if (s.size == 0 || !s.str) return { nullptr, 0 }; char *buf = push_array_no_zero(arena, char, s.size + 1); MemoryCopy(buf, s.str, s.size); buf[s.size] = 0; - return {buf, s.size}; + return { buf, s.size }; } void str8_list_push(Arena *arena, Str8List *list, Str8 s) { Str8Node *node = push_array(arena, Str8Node, 1); - node->string = s; + node->string = s; SLLQueuePush(list->first, list->last, node); list->count++; list->total_size += s.size; diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 187e303..bae722c 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -31,9 +31,9 @@ struct Arena; //////////////////////////////// // Constructors -static inline Str8 str8(const char *s, U64 len) { return {s, len}; } -static inline Str8 str8_cstr(const char *s) { return {s, s ? (U64)strlen(s) : 0}; } -static inline Str8 str8_lit(const char *s) { return {s, s ? (U64)strlen(s) : 0}; } +static inline Str8 str8(const char *s, U64 len) { return { s, len }; } +static inline Str8 str8_cstr(const char *s) { return { s, s ? (U64)strlen(s) : 0 }; } +static inline Str8 str8_lit(const char *s) { return { s, s ? (U64)strlen(s) : 0 }; } static inline B32 str8_match(Str8 a, Str8 b) { if (a.size != b.size) return 0; return MemoryCompare(a.str, b.str, a.size) == 0; diff --git a/src/main.cpp b/src/main.cpp index d4fc0b6..dad9ae5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,34 +4,34 @@ #include #endif // [h] +#include "audio/audio.h" #include "base/base_inc.h" +#include "midi/midi.h" #include "platform/platform.h" #include "renderer/renderer.h" -#include "midi/midi.h" -#include "audio/audio.h" #include "ui/ui_core.h" #include "ui/ui_icons.h" -#include "ui/ui_widgets.h" -#include "ui/ui_popups.h" #include "ui/ui_piano.h" +#include "ui/ui_popups.h" +#include "ui/ui_widgets.h" // [cpp] #include "base/base_inc.cpp" #include "ui/ui_core.cpp" #include "ui/ui_icons.cpp" -#include "ui/ui_widgets.cpp" -#include "ui/ui_popups.cpp" #include "ui/ui_piano.cpp" +#include "ui/ui_popups.cpp" +#include "ui/ui_widgets.cpp" #ifdef __APPLE__ +#include "audio/audio_coreaudio.cpp" +#include "midi/midi_coremidi.cpp" #include "platform/platform_macos.mm" #include "renderer/renderer_metal.mm" -#include "midi/midi_coremidi.cpp" -#include "audio/audio_coreaudio.cpp" #else +#include "audio/audio_asio.cpp" +#include "midi/midi_win32.cpp" #include "platform/platform_win32.cpp" #include "renderer/renderer_vulkan.cpp" -#include "midi/midi_win32.cpp" -#include "audio/audio_asio.cpp" #endif #include "menus.cpp" @@ -43,20 +43,20 @@ static Clay_TextElementConfig g_text_config_title; static Clay_TextElementConfig g_text_config_dim; static void init_text_configs() { - g_text_config_normal = {}; + g_text_config_normal = {}; g_text_config_normal.textColor = g_theme.text; - g_text_config_normal.fontSize = 15; - g_text_config_normal.wrapMode = CLAY_TEXT_WRAP_NONE; + g_text_config_normal.fontSize = 15; + g_text_config_normal.wrapMode = CLAY_TEXT_WRAP_NONE; - g_text_config_title = {}; + g_text_config_title = {}; g_text_config_title.textColor = g_theme.text; - g_text_config_title.fontSize = 15; - g_text_config_title.wrapMode = CLAY_TEXT_WRAP_NONE; + g_text_config_title.fontSize = 15; + g_text_config_title.wrapMode = CLAY_TEXT_WRAP_NONE; - g_text_config_dim = {}; + g_text_config_dim = {}; g_text_config_dim.textColor = g_theme.text_dim; - g_text_config_dim.fontSize = 15; - g_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; + g_text_config_dim.fontSize = 15; + g_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; } //////////////////////////////// @@ -74,96 +74,96 @@ struct AppState { B32 show_log; B32 show_midi_devices; #ifdef __APPLE__ - U64 freq_numer; - U64 freq_denom; - U64 last_time; + U64 freq_numer; + U64 freq_denom; + U64 last_time; #else - LARGE_INTEGER freq; - LARGE_INTEGER last_time; + LARGE_INTEGER freq; + LARGE_INTEGER last_time; #endif // Tab state - S32 right_panel_tab; // 0 = Properties, 1 = MIDI Devices - S32 bottom_panel_tab; // 0 = Item Editor, 1 = Sample Mapper + S32 right_panel_tab; // 0 = Properties, 1 = MIDI Devices + S32 bottom_panel_tab; // 0 = Item Editor, 1 = Sample Mapper // Piano state - UI_PianoState piano_state; + UI_PianoState piano_state; // Demo widget state - B32 demo_checkbox_a; - B32 demo_checkbox_b; - S32 demo_radio_sel; - S32 demo_dropdown_sel; - char demo_text_a[128]; - char demo_text_b[128]; - S32 demo_button_count; + B32 demo_checkbox_a; + B32 demo_checkbox_b; + S32 demo_radio_sel; + S32 demo_dropdown_sel; + char demo_text_a[128]; + char demo_text_b[128]; + S32 demo_button_count; // Modal / window demo state - B32 show_settings_window; - B32 show_about_window; - B32 show_confirm_dialog; + B32 show_settings_window; + B32 show_about_window; + B32 show_confirm_dialog; // Pop-out windows - B32 show_mix_popout; - B32 show_patch_popout; - S32 settings_theme_sel; - S32 settings_theme_prev; - B32 settings_vsync; - B32 settings_autosave; + B32 show_mix_popout; + B32 show_patch_popout; + S32 settings_theme_sel; + S32 settings_theme_prev; + B32 settings_vsync; + B32 settings_autosave; // Accent color selection - S32 accent_sel; - S32 accent_prev; + S32 accent_sel; + S32 accent_prev; // Corner radius selection - S32 radius_sel; + S32 radius_sel; // Settings dialog tab - S32 settings_tab; // 0=Audio, 1=Midi, 2=Keyboard Shortcuts, 3=Appearance + S32 settings_tab; // 0=Audio, 1=Midi, 2=Keyboard Shortcuts, 3=Appearance // Knob demo state - F32 demo_knob_unsigned; - F32 demo_knob_signed; + F32 demo_knob_unsigned; + F32 demo_knob_signed; // Slider/fader demo state - F32 demo_slider_h; - F32 demo_slider_v; - F32 demo_fader; + F32 demo_slider_h; + F32 demo_slider_v; + F32 demo_fader; // Scrollbar drag state - B32 scrollbar_dragging; - F32 scrollbar_drag_start_y; - F32 scrollbar_drag_start_scroll; + B32 scrollbar_dragging; + F32 scrollbar_drag_start_y; + F32 scrollbar_drag_start_scroll; // Audio device selection - S32 audio_device_sel; // dropdown index: 0 = None, 1+ = device - S32 audio_device_prev; // previous selection for change detection + S32 audio_device_sel; // dropdown index: 0 = None, 1+ = device + S32 audio_device_prev; // previous selection for change detection // UI scale (Cmd+/Cmd- to zoom) - F32 ui_scale; + F32 ui_scale; // Panel sizes (in unscaled px, will be multiplied by uis()) - F32 browser_width; // default 200 - F32 right_col_width; // default 250 - F32 log_height; // default 180 + F32 browser_width; // default 200 + F32 right_col_width; // default 250 + F32 log_height; // default 180 // Panel drag state - S32 panel_drag; // 0=none, 1=browser, 2=right, 3=log - F32 panel_drag_start_mouse; // mouse X or Y when drag started - F32 panel_drag_start_size; // panel size when drag started + S32 panel_drag; // 0=none, 1=browser, 2=right, 3=log + F32 panel_drag_start_mouse; // mouse X or Y when drag started + F32 panel_drag_start_size; // panel size when drag started // Master layout: 0 = Edit, 1 = Mix, 2 = Patch - S32 master_layout; + S32 master_layout; // Mix view fader state (8 channels + master) - F32 mix_faders[8]; - F32 mix_pans[8]; - F32 mix_master_pan; + F32 mix_faders[8]; + F32 mix_pans[8]; + F32 mix_master_pan; // Patch view state - S32 patch_tab; // 0 = Matrix, 1 = Graph - B32 patch_matrix[32][33]; // internal routing: 32 inputs x (Master + 32 outputs) - B32 hw_matrix[32][32]; // hardware routing: 32 channel outputs x 32 hw outputs + S32 patch_tab; // 0 = Matrix, 1 = Graph + B32 patch_matrix[32][33]; // internal routing: 32 inputs x (Master + 32 outputs) + B32 hw_matrix[32][32]; // hardware routing: 32 channel outputs x 32 hw outputs }; //////////////////////////////// @@ -173,333 +173,304 @@ struct AppState { static void build_browser_panel(AppState *app) { if (!app->show_browser) return; - Clay_Color bp_top = g_theme.bg_medium; - Clay_Color bp_bot = {(F32)Max((S32)bp_top.r-8,0), (F32)Max((S32)bp_top.g-8,0), (F32)Max((S32)bp_top.b-8,0), 255}; + Clay_Color bp_top = g_theme.bg_medium; + Clay_Color bp_bot = { (F32)Max((S32)bp_top.r - 8, 0), (F32)Max((S32)bp_top.g - 8, 0), (F32)Max((S32)bp_top.b - 8, 0), 255 }; CustomGradientData *bp_grad = alloc_gradient(bp_top, bp_bot); CLAY(CLAY_ID("BrowserPanel"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(app->browser_width)), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = bp_top, - .custom = { .customData = bp_grad }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(app->browser_width)), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = bp_top, .custom = { .customData = bp_grad }, ) { { - S32 sel = 0; + S32 sel = 0; static const char *browser_tabs[] = { "Browser" }; ui_tab_bar("BrowserTabRow", browser_tabs, 1, &sel); } CLAY(CLAY_ID("BrowserContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(6), uip(6) }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(6), uip(6) }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Top highlight (beveled edge) CLAY(CLAY_ID("BrowserHighlight"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.bg_lighter - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.bg_lighter) {} CLAY_TEXT(CLAY_STRING("Instruments"), &g_text_config_normal); } } } static void build_main_panel(AppState *app) { - Clay_Color mp_top = g_theme.bg_light; - Clay_Color mp_bot = {(F32)Max((S32)mp_top.r-8,0), (F32)Max((S32)mp_top.g-8,0), (F32)Max((S32)mp_top.b-8,0), 255}; + Clay_Color mp_top = g_theme.bg_light; + Clay_Color mp_bot = { (F32)Max((S32)mp_top.r - 8, 0), (F32)Max((S32)mp_top.g - 8, 0), (F32)Max((S32)mp_top.b - 8, 0), 255 }; CustomGradientData *mp_grad = alloc_gradient(mp_top, mp_bot); CLAY(CLAY_ID("MainPanel"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = mp_top, - .custom = { .customData = mp_grad } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = mp_top, .custom = { .customData = mp_grad }) { { - S32 sel = 0; + S32 sel = 0; static const char *main_tabs[] = { "Main" }; ui_tab_bar("MainTabRow", main_tabs, 1, &sel); } CLAY(CLAY_ID("MainScrollArea"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - CLAY(CLAY_ID("MainContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(16), uip(16), uip(12), uip(12) }, - .childGap = uip(12), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }, - ) { - // Top highlight (beveled edge) - CLAY(CLAY_ID("MainHighlight"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.bg_lighter - ) {} - // Section: Buttons - ui_label("LblButtons", "Buttons"); - CLAY(CLAY_ID("ButtonRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(8), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - if (ui_button("BtnHello", "Click Me")) { - app->demo_button_count++; - } - if (ui_button("BtnReset", "Reset")) { - app->demo_button_count = 0; - } - } - // Show click count - static char btn_count_buf[64]; - snprintf(btn_count_buf, sizeof(btn_count_buf), "Button clicked %d times", app->demo_button_count); - ui_label("LblBtnCount", btn_count_buf); - - // Separator - CLAY(CLAY_ID("Sep1"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} - - // Section: Checkboxes - ui_label("LblCheckboxes", "Checkboxes"); - ui_checkbox("ChkA", "Enable feature A", &app->demo_checkbox_a); - ui_checkbox("ChkB", "Enable feature B", &app->demo_checkbox_b); - - CLAY(CLAY_ID("Sep2"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} - - // Section: Radio buttons - ui_label("LblRadio", "Output Format"); - static const char *radio_options[] = { "WAV", "AIFF", "FLAC" }; - ui_radio_group("RadioFmt", radio_options, 3, &app->demo_radio_sel); - - CLAY(CLAY_ID("Sep3"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} - - // Section: Text inputs - ui_label("LblText", "Text Inputs"); - CLAY(CLAY_ID("TextRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(8), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - CLAY(CLAY_ID("TextCol1"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + CLAY(CLAY_ID("MainContent"), + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(16), uip(16), uip(12), uip(12) }, + .childGap = uip(12), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }, ) { + // Top highlight (beveled edge) + CLAY(CLAY_ID("MainHighlight"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.bg_lighter) {} + // Section: Buttons + ui_label("LblButtons", "Buttons"); + CLAY(CLAY_ID("ButtonRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(8), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + if (ui_button("BtnHello", "Click Me")) { + app->demo_button_count++; } - ) { - ui_label("LblName", "Name:"); - ui_text_input("TxtName", app->demo_text_a, sizeof(app->demo_text_a)); - } - CLAY(CLAY_ID("TextCol2"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, + if (ui_button("BtnReset", "Reset")) { + app->demo_button_count = 0; + } + } + // Show click count + static char btn_count_buf[64]; + snprintf(btn_count_buf, sizeof(btn_count_buf), "Button clicked %d times", app->demo_button_count); + ui_label("LblBtnCount", btn_count_buf); + + // Separator + CLAY(CLAY_ID("Sep1"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Checkboxes + ui_label("LblCheckboxes", "Checkboxes"); + ui_checkbox("ChkA", "Enable feature A", &app->demo_checkbox_a); + ui_checkbox("ChkB", "Enable feature B", &app->demo_checkbox_b); + + CLAY(CLAY_ID("Sep2"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Radio buttons + ui_label("LblRadio", "Output Format"); + static const char *radio_options[] = { "WAV", "AIFF", "FLAC" }; + ui_radio_group("RadioFmt", radio_options, 3, &app->demo_radio_sel); + + CLAY(CLAY_ID("Sep3"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Text inputs + ui_label("LblText", "Text Inputs"); + CLAY(CLAY_ID("TextRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(8), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + CLAY(CLAY_ID("TextCol1"), + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { + ui_label("LblName", "Name:"); + ui_text_input("TxtName", app->demo_text_a, sizeof(app->demo_text_a)); + } + CLAY(CLAY_ID("TextCol2"), + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { + ui_label("LblPath", "Output Path:"); + ui_text_input("TxtPath", app->demo_text_b, sizeof(app->demo_text_b)); + } + } + + CLAY(CLAY_ID("Sep4"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Dropdown + ui_label("LblDropdown", "Sample Rate"); + CLAY(CLAY_ID("DropdownWrapper"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(200)), .height = CLAY_SIZING_FIT() }, + }) { + static const char *rate_options[] = { "44100 Hz", "48000 Hz", "88200 Hz", "96000 Hz", "192000 Hz" }; + ui_dropdown("DropRate", rate_options, 5, &app->demo_dropdown_sel); + } + + CLAY(CLAY_ID("Sep5"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Knobs + ui_label("LblKnobs", "Knobs"); + CLAY(CLAY_ID("KnobRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(16), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + ui_knob("KnobVolume", "Volume", &app->demo_knob_unsigned, 100.0f, 0, 75.0f, 1); + ui_knob("KnobPan", "Pan", &app->demo_knob_signed, 50.0f, 1, 0.0f, 1); + } + + CLAY(CLAY_ID("Sep6"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Sliders + ui_label("LblSliders", "Sliders"); + CLAY(CLAY_ID("SliderHRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(16), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + ui_slider_h("SliderH", "Horizontal", &app->demo_slider_h, 100.0f, 0, 50.0f, 1); + } + CLAY(CLAY_ID("SliderVRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(24), + .childAlignment = { .y = CLAY_ALIGN_Y_TOP }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + ui_slider_v("SliderV", "Vertical", &app->demo_slider_v, 100.0f, 0, 75.0f, 1); + ui_fader("Fader1", "Fader", &app->demo_fader, 50.0f, 1, 0.0f, 1); + } + + CLAY(CLAY_ID("Sep7"), + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} + + // Section: Windows & Modals + ui_label("LblWindows", "Windows & Modals"); + CLAY(CLAY_ID("WindowBtnRow"), + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(8), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + if (ui_button("BtnAbout", "About")) { + app->show_about_window = 1; + } + if (ui_button("BtnConfirm", "Confirm Dialog")) { + app->show_confirm_dialog = 1; } - ) { - ui_label("LblPath", "Output Path:"); - ui_text_input("TxtPath", app->demo_text_b, sizeof(app->demo_text_b)); } } - CLAY(CLAY_ID("Sep4"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} + // Scrollbar + { + Clay_ScrollContainerData scroll_data = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + if (scroll_data.found && scroll_data.contentDimensions.height > scroll_data.scrollContainerDimensions.height) { + F32 track_h = scroll_data.scrollContainerDimensions.height; + F32 content_h = scroll_data.contentDimensions.height; + F32 visible_ratio = track_h / content_h; + F32 thumb_h = Max(visible_ratio * track_h, uis(24)); + F32 scroll_range = content_h - track_h; + F32 scroll_pct = scroll_range > 0 ? -scroll_data.scrollPosition->y / scroll_range : 0; + F32 thumb_y = scroll_pct * (track_h - thumb_h); + F32 bar_w = uis(8); - // Section: Dropdown - ui_label("LblDropdown", "Sample Rate"); - CLAY(CLAY_ID("DropdownWrapper"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(200)), .height = CLAY_SIZING_FIT() }, - } - ) { - static const char *rate_options[] = { "44100 Hz", "48000 Hz", "88200 Hz", "96000 Hz", "192000 Hz" }; - ui_dropdown("DropRate", rate_options, 5, &app->demo_dropdown_sel); - } + // Handle scrollbar drag + Clay_ElementId thumb_id = CLAY_ID("MainScrollThumb"); + Clay_ElementId track_id = CLAY_ID("MainScrollTrack"); + B32 thumb_hovered = Clay_PointerOver(thumb_id); + B32 track_hovered = Clay_PointerOver(track_id); + PlatformInput input = g_wstate.input; + B32 mouse_clicked = input.mouse_down && !input.was_mouse_down; - CLAY(CLAY_ID("Sep5"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} + if (mouse_clicked && thumb_hovered) { + app->scrollbar_dragging = true; + app->scrollbar_drag_start_y = input.mouse_pos.y; + app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y; + } else if (mouse_clicked && track_hovered && !thumb_hovered) { + // Click on track: jump scroll position so thumb centers on click + Clay_BoundingBox track_bb = Clay_GetElementData(track_id).boundingBox; + F32 click_rel = input.mouse_pos.y - track_bb.y; + F32 target_pct = (click_rel - thumb_h / 2) / (track_h - thumb_h); + if (target_pct < 0) target_pct = 0; + if (target_pct > 1) target_pct = 1; + scroll_data.scrollPosition->y = -target_pct * scroll_range; + // Start dragging from new position + app->scrollbar_dragging = true; + app->scrollbar_drag_start_y = input.mouse_pos.y; + app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y; + } - // Section: Knobs - ui_label("LblKnobs", "Knobs"); - CLAY(CLAY_ID("KnobRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(16), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - ui_knob("KnobVolume", "Volume", &app->demo_knob_unsigned, 100.0f, 0, 75.0f, 1); - ui_knob("KnobPan", "Pan", &app->demo_knob_signed, 50.0f, 1, 0.0f, 1); - } + if (!input.mouse_down) { + app->scrollbar_dragging = false; + } - CLAY(CLAY_ID("Sep6"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} + if (app->scrollbar_dragging) { + F32 dy = input.mouse_pos.y - app->scrollbar_drag_start_y; + F32 scroll_per_px = scroll_range / (track_h - thumb_h); + F32 new_scroll = app->scrollbar_drag_start_scroll - dy * scroll_per_px; + if (new_scroll > 0) new_scroll = 0; + if (new_scroll < -scroll_range) new_scroll = -scroll_range; + scroll_data.scrollPosition->y = new_scroll; + } - // Section: Sliders - ui_label("LblSliders", "Sliders"); - CLAY(CLAY_ID("SliderHRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(16), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - ui_slider_h("SliderH", "Horizontal", &app->demo_slider_h, 100.0f, 0, 50.0f, 1); - } - CLAY(CLAY_ID("SliderVRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(24), - .childAlignment = { .y = CLAY_ALIGN_Y_TOP }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - ui_slider_v("SliderV", "Vertical", &app->demo_slider_v, 100.0f, 0, 75.0f, 1); - ui_fader("Fader1", "Fader", &app->demo_fader, 50.0f, 1, 0.0f, 1); - } + // Thumb color: highlight on hover or drag + Clay_Color thumb_color = g_theme.scrollbar_grab; + if (app->scrollbar_dragging || thumb_hovered) { + thumb_color = Clay_Color{ + (F32)Min((S32)thumb_color.r + 30, 255), + (F32)Min((S32)thumb_color.g + 30, 255), + (F32)Min((S32)thumb_color.b + 30, 255), + thumb_color.a + }; + } - CLAY(CLAY_ID("Sep7"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} - - // Section: Windows & Modals - ui_label("LblWindows", "Windows & Modals"); - CLAY(CLAY_ID("WindowBtnRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(8), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - if (ui_button("BtnAbout", "About")) { - app->show_about_window = 1; - } - if (ui_button("BtnConfirm", "Confirm Dialog")) { - app->show_confirm_dialog = 1; - } - } - } - - // Scrollbar - { - Clay_ScrollContainerData scroll_data = Clay_GetScrollContainerData(CLAY_ID("MainContent")); - if (scroll_data.found && scroll_data.contentDimensions.height > scroll_data.scrollContainerDimensions.height) { - F32 track_h = scroll_data.scrollContainerDimensions.height; - F32 content_h = scroll_data.contentDimensions.height; - F32 visible_ratio = track_h / content_h; - F32 thumb_h = Max(visible_ratio * track_h, uis(24)); - F32 scroll_range = content_h - track_h; - F32 scroll_pct = scroll_range > 0 ? -scroll_data.scrollPosition->y / scroll_range : 0; - F32 thumb_y = scroll_pct * (track_h - thumb_h); - F32 bar_w = uis(8); - - // Handle scrollbar drag - Clay_ElementId thumb_id = CLAY_ID("MainScrollThumb"); - Clay_ElementId track_id = CLAY_ID("MainScrollTrack"); - B32 thumb_hovered = Clay_PointerOver(thumb_id); - B32 track_hovered = Clay_PointerOver(track_id); - PlatformInput input = g_wstate.input; - B32 mouse_clicked = input.mouse_down && !input.was_mouse_down; - - if (mouse_clicked && thumb_hovered) { - app->scrollbar_dragging = true; - app->scrollbar_drag_start_y = input.mouse_pos.y; - app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y; - } else if (mouse_clicked && track_hovered && !thumb_hovered) { - // Click on track: jump scroll position so thumb centers on click - Clay_BoundingBox track_bb = Clay_GetElementData(track_id).boundingBox; - F32 click_rel = input.mouse_pos.y - track_bb.y; - F32 target_pct = (click_rel - thumb_h / 2) / (track_h - thumb_h); - if (target_pct < 0) target_pct = 0; - if (target_pct > 1) target_pct = 1; - scroll_data.scrollPosition->y = -target_pct * scroll_range; - // Start dragging from new position - app->scrollbar_dragging = true; - app->scrollbar_drag_start_y = input.mouse_pos.y; - app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y; - } - - if (!input.mouse_down) { + CLAY(track_id, + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(bar_w), .height = CLAY_SIZING_GROW() }, + }, + .backgroundColor = g_theme.scrollbar_bg) { + CLAY(thumb_id, + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(thumb_h) }, + }, + .backgroundColor = thumb_color, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .floating = { + .offset = { 0, thumb_y }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .attachTo = CLAY_ATTACH_TO_PARENT, + }, ) {} + } + } else { app->scrollbar_dragging = false; } - - if (app->scrollbar_dragging) { - F32 dy = input.mouse_pos.y - app->scrollbar_drag_start_y; - F32 scroll_per_px = scroll_range / (track_h - thumb_h); - F32 new_scroll = app->scrollbar_drag_start_scroll - dy * scroll_per_px; - if (new_scroll > 0) new_scroll = 0; - if (new_scroll < -scroll_range) new_scroll = -scroll_range; - scroll_data.scrollPosition->y = new_scroll; - } - - // Thumb color: highlight on hover or drag - Clay_Color thumb_color = g_theme.scrollbar_grab; - if (app->scrollbar_dragging || thumb_hovered) { - thumb_color = Clay_Color{ - (F32)Min((S32)thumb_color.r + 30, 255), - (F32)Min((S32)thumb_color.g + 30, 255), - (F32)Min((S32)thumb_color.b + 30, 255), - thumb_color.a - }; - } - - CLAY(track_id, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(bar_w), .height = CLAY_SIZING_GROW() }, - }, - .backgroundColor = g_theme.scrollbar_bg - ) { - CLAY(thumb_id, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(thumb_h) }, - }, - .backgroundColor = thumb_color, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .floating = { - .offset = { 0, thumb_y }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .attachTo = CLAY_ATTACH_TO_PARENT, - }, - ) {} - } - } else { - app->scrollbar_dragging = false; } - } } // MainScrollArea } } @@ -507,65 +478,57 @@ static void build_main_panel(AppState *app) { static void build_right_panel(AppState *app) { static const char *right_tabs[] = { "Properties", "MIDI Devices" }; - Clay_Color rp_top = g_theme.bg_medium; - Clay_Color rp_bot = {(F32)Max((S32)rp_top.r-8,0), (F32)Max((S32)rp_top.g-8,0), (F32)Max((S32)rp_top.b-8,0), 255}; + Clay_Color rp_top = g_theme.bg_medium; + Clay_Color rp_bot = { (F32)Max((S32)rp_top.r - 8, 0), (F32)Max((S32)rp_top.g - 8, 0), (F32)Max((S32)rp_top.b - 8, 0), 255 }; CustomGradientData *rp_grad = alloc_gradient(rp_top, rp_bot); CLAY(CLAY_ID("RightPanel"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = rp_top, - .custom = { .customData = rp_grad } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = rp_top, .custom = { .customData = rp_grad }) { ui_tab_bar("RightTabs", right_tabs, 2, &app->right_panel_tab); if (app->right_panel_tab == 0) { // Properties content CLAY(CLAY_ID("PropertiesContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(6), uip(6) }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(6), uip(6) }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Top highlight (beveled edge) CLAY(CLAY_ID("PropsHighlight"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.bg_lighter - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.bg_lighter) {} CLAY_TEXT(CLAY_STRING("Details"), &g_text_config_normal); } } else { // MIDI Devices content MidiEngine *midi = app->midi; CLAY(CLAY_ID("MidiContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(6), uip(6) }, - .childGap = uip(6), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(6), uip(6) }, + .childGap = uip(6), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Top highlight (beveled edge) CLAY(CLAY_ID("MidiHighlight"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.bg_lighter - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.bg_lighter) {} // Refresh button - Clay_ElementId refresh_eid = CLAY_ID("MidiRefreshBtn"); - B32 refresh_hovered = Clay_PointerOver(refresh_eid); + Clay_ElementId refresh_eid = CLAY_ID("MidiRefreshBtn"); + B32 refresh_hovered = Clay_PointerOver(refresh_eid); CLAY(refresh_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, - .padding = { uip(12), uip(12), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = refresh_hovered ? g_theme.accent_hover : g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, + .padding = { uip(12), uip(12), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = refresh_hovered ? g_theme.accent_hover : g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS)) { CLAY_TEXT(CLAY_STRING("Refresh"), &g_text_config_normal); } if (refresh_hovered && g_wstate.mouse_clicked) { @@ -573,19 +536,19 @@ static void build_right_panel(AppState *app) { } static char device_bufs[64][128]; - S32 device_count = midi_get_device_count(midi); + S32 device_count = midi_get_device_count(midi); // --- Inputs section --- CLAY_TEXT(CLAY_STRING("Inputs"), &g_text_config_dim); - static const char *note_names[] = {"C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B"}; - static char note_bufs[64][8]; - static char vel_bufs[64][8]; + static const char *note_names[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" }; + static char note_bufs[64][8]; + static char vel_bufs[64][8]; static Clay_TextElementConfig box_text_config; - box_text_config = {}; - box_text_config.textColor = Clay_Color{255, 255, 255, 255}; - box_text_config.fontSize = FONT_SIZE_SMALL; - box_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; + box_text_config = {}; + box_text_config.textColor = Clay_Color{ 255, 255, 255, 255 }; + box_text_config.fontSize = FONT_SIZE_SMALL; + box_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; B32 has_inputs = 0; for (S32 i = 0; i < device_count && i < 64; i++) { @@ -593,7 +556,7 @@ static void build_right_panel(AppState *app) { if (!dev->is_input) continue; has_inputs = 1; - S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name); + S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name); Clay_String device_str = { .isStaticallyAllocated = false, .length = len, .chars = device_bufs[i] }; // Velocity-based color: blue (vel 0) → green (mid) → red (vel 127) @@ -602,17 +565,17 @@ static void build_right_panel(AppState *app) { if (dev->active) { box_color = piano_velocity_color(dev->velocity); } else if (dev->releasing) { - box_color = Clay_Color{255, 255, 255, 255}; + box_color = Clay_Color{ 255, 255, 255, 255 }; } else { - box_color = Clay_Color{60, 60, 60, 255}; + box_color = Clay_Color{ 60, 60, 60, 255 }; } // Box text: note name when held, "OFF" when releasing, "---" when idle S32 nlen; if (dev->active) { - S32 pitch = dev->note % 12; + S32 pitch = dev->note % 12; S32 octave = (dev->note / 12) - 1; - nlen = snprintf(note_bufs[i], sizeof(note_bufs[i]), "%s%d", note_names[pitch], octave); + nlen = snprintf(note_bufs[i], sizeof(note_bufs[i]), "%s%d", note_names[pitch], octave); } else if (dev->releasing) { nlen = snprintf(note_bufs[i], sizeof(note_bufs[i]), "OFF"); } else { @@ -621,10 +584,10 @@ static void build_right_panel(AppState *app) { Clay_String note_str = { .isStaticallyAllocated = false, .length = nlen, .chars = note_bufs[i] }; // Box text color: dark for white bg (releasing), white otherwise - Clay_TextElementConfig *box_txt = &box_text_config; + Clay_TextElementConfig *box_txt = &box_text_config; static Clay_TextElementConfig box_text_dark; - box_text_dark = box_text_config; - box_text_dark.textColor = Clay_Color{30, 30, 30, 255}; + box_text_dark = box_text_config; + box_text_dark.textColor = Clay_Color{ 30, 30, 30, 255 }; if (dev->releasing) box_txt = &box_text_dark; // Velocity text @@ -636,23 +599,20 @@ static void build_right_panel(AppState *app) { Clay_String vel_str = { .isStaticallyAllocated = false, .length = vlen, .chars = vel_bufs[i] }; CLAY(CLAY_IDI("MidiIn", i), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .padding = { uip(4), uip(4), uip(2), uip(2) }, - .childGap = uip(6), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .padding = { uip(4), uip(4), uip(2), uip(2) }, + .childGap = uip(6), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Note name box (colored by velocity) CLAY(CLAY_IDI("MidiInNote", i), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(36)), .height = CLAY_SIZING_FIXED(uis(18)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = box_color, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(36)), .height = CLAY_SIZING_FIXED(uis(18)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = box_color, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS)) { CLAY_TEXT(note_str, box_txt); } // Velocity number @@ -674,15 +634,14 @@ static void build_right_panel(AppState *app) { if (dev->is_input) continue; has_outputs = 1; - S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name); + S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name); Clay_String device_str = { .isStaticallyAllocated = false, .length = len, .chars = device_bufs[i] }; CLAY(CLAY_IDI("MidiOut", i), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .padding = { uip(4), uip(4), uip(2), uip(2) }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .padding = { uip(4), uip(4), uip(2), uip(2) }, + }) { CLAY_TEXT(device_str, &g_text_config_normal); } } @@ -697,46 +656,41 @@ static void build_right_panel(AppState *app) { static void build_log_panel(AppState *app) { if (!app->show_log) return; - Clay_Color lp_top = g_theme.bg_medium; - Clay_Color lp_bot = {(F32)Max((S32)lp_top.r-8,0), (F32)Max((S32)lp_top.g-8,0), (F32)Max((S32)lp_top.b-8,0), 255}; + Clay_Color lp_top = g_theme.bg_medium; + Clay_Color lp_bot = { (F32)Max((S32)lp_top.r - 8, 0), (F32)Max((S32)lp_top.g - 8, 0), (F32)Max((S32)lp_top.b - 8, 0), 255 }; CustomGradientData *lp_grad = alloc_gradient(lp_top, lp_bot); CLAY(CLAY_ID("LogPanel"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(app->log_height)) }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = lp_top, - .custom = { .customData = lp_grad }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(app->log_height)) }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = lp_top, .custom = { .customData = lp_grad }, ) { static const char *bottom_tabs[] = { "Item Editor", "Sample Mapper" }; ui_tab_bar("BottomTabRow", bottom_tabs, 2, &app->bottom_panel_tab); if (app->bottom_panel_tab == 0) { // Item Editor tab CLAY(CLAY_ID("ItemEditorContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(6), uip(6) }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(6), uip(6) }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { CLAY(CLAY_ID("ItemEditorHighlight"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.bg_lighter - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.bg_lighter) {} CLAY_TEXT(CLAY_STRING("Item Editor"), &g_text_config_normal); } } else { // Sample Mapper tab — 88-key piano CLAY(CLAY_ID("SampleMapperContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(4), uip(4), uip(4), uip(4) }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(4), uip(4), uip(4), uip(4) }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { F32 piano_avail_h = uis(app->log_height) - TAB_HEIGHT - uip(8); F32 piano_avail_w = (F32)app->last_w - uip(8); ui_piano(&app->piano_state, app->midi, piano_avail_w, piano_avail_h); @@ -759,28 +713,26 @@ static void settings_window_content(void *user_data) { // Tab content area with some top padding CLAY(CLAY_ID("SettingsTabContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .padding = { 0, 0, uip(8), 0 }, - .childGap = uip(6), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .padding = { 0, 0, uip(8), 0 }, + .childGap = uip(6), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { if (app->settings_tab == 0) { // === Audio tab === ui_label("SettingsLblAudio", "Audio Device"); static const char *audio_options[AUDIO_MAX_DEVICES + 1]; - S32 audio_count = audio_get_device_count(app->audio); - audio_options[0] = "None"; + S32 audio_count = audio_get_device_count(app->audio); + audio_options[0] = "None"; for (S32 i = 0; i < audio_count && i < AUDIO_MAX_DEVICES; i++) { AudioDeviceInfo *dev = audio_get_device(app->audio, i); audio_options[i + 1] = dev ? dev->name : "???"; } CLAY(CLAY_ID("SettingsAudioWrap"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(260)), .height = CLAY_SIZING_FIT() } } - ) { + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(260)), .height = CLAY_SIZING_FIT() } }) { ui_dropdown("SettingsAudio", audio_options, audio_count + 1, &app->audio_device_sel); } @@ -793,14 +745,13 @@ static void settings_window_content(void *user_data) { } CLAY(CLAY_ID("SettingsAudioBtnRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(8), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { - B32 device_open = (app->audio_device_sel > 0); + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(8), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { + B32 device_open = (app->audio_device_sel > 0); B32 tone_playing = audio_is_test_tone_playing(app->audio); if (device_open && !tone_playing) { @@ -810,18 +761,16 @@ static void settings_window_content(void *user_data) { } else { Clay_ElementId btn_eid = CLAY_ID("BtnTestToneDisabled"); CLAY(btn_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, - .padding = { uip(12), uip(12), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.disabled_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, + .padding = { uip(12), uip(12), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.disabled_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS)) { static Clay_TextElementConfig disabled_text = {}; - disabled_text.textColor = g_theme.disabled_text; - disabled_text.fontSize = FONT_SIZE_NORMAL; - disabled_text.wrapMode = CLAY_TEXT_WRAP_NONE; + disabled_text.textColor = g_theme.disabled_text; + disabled_text.fontSize = FONT_SIZE_NORMAL; + disabled_text.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY_TEXT(CLAY_STRING("Play Test Tone"), &disabled_text); } } @@ -846,24 +795,21 @@ static void settings_window_content(void *user_data) { ui_label("SettingsLblTheme", "Theme"); static const char *theme_options[] = { "Dark", "Light" }; CLAY(CLAY_ID("SettingsThemeWrap"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } } - ) { + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } }) { ui_dropdown("SettingsTheme", theme_options, 2, &app->settings_theme_sel); } ui_label("SettingsLblAccent", "Accent Color"); static const char *accent_options[] = { "Blue", "Turquoise", "Orange", "Purple", "Pink", "Red", "Green" }; CLAY(CLAY_ID("SettingsAccentWrap"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } } - ) { + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } }) { ui_dropdown("SettingsAccent", accent_options, 7, &app->accent_sel); } ui_label("SettingsLblRadius", "Corner Radius"); static const char *radius_options[] = { "None", "Small", "Medium", "Large" }; CLAY(CLAY_ID("SettingsRadiusWrap"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } } - ) { + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(220)), .height = CLAY_SIZING_FIT() } }) { if (ui_dropdown("SettingsRadius", radius_options, 4, &app->radius_sel)) { g_theme.corner_radius = radius_values[app->radius_sel]; } @@ -885,11 +831,14 @@ static void about_window_content(void *user_data) { // Panel splitter drag logic (called each frame before build_ui) static void update_panel_splitters(AppState *app) { - if (app->master_layout != 0) { platform_set_cursor(PLATFORM_CURSOR_ARROW); return; } - PlatformInput input = g_wstate.input; - B32 mouse_clicked = input.mouse_down && !input.was_mouse_down; - B32 mouse_released = !input.mouse_down && input.was_mouse_down; - PlatformCursor cursor = PLATFORM_CURSOR_ARROW; + if (app->master_layout != 0) { + platform_set_cursor(PLATFORM_CURSOR_ARROW); + return; + } + PlatformInput input = g_wstate.input; + B32 mouse_clicked = input.mouse_down && !input.was_mouse_down; + B32 mouse_released = !input.mouse_down && input.was_mouse_down; + PlatformCursor cursor = PLATFORM_CURSOR_ARROW; // Check hover on splitter elements with expanded hit area (splitters are 1px) F32 grab_margin = uis(3); @@ -900,7 +849,7 @@ static void update_panel_splitters(AppState *app) { Clay_ElementData d = Clay_GetElementData(CLAY_ID("SplitBrowser")); if (d.found) { Clay_BoundingBox b = d.boundingBox; - hover_browser = mx >= b.x - grab_margin && mx <= b.x + b.width + grab_margin && + hover_browser = mx >= b.x - grab_margin && mx <= b.x + b.width + grab_margin && my >= b.y && my <= b.y + b.height; } } @@ -908,7 +857,7 @@ static void update_panel_splitters(AppState *app) { Clay_ElementData d = Clay_GetElementData(CLAY_ID("SplitRight")); if (d.found) { Clay_BoundingBox b = d.boundingBox; - hover_right = mx >= b.x - grab_margin && mx <= b.x + b.width + grab_margin && + hover_right = mx >= b.x - grab_margin && mx <= b.x + b.width + grab_margin && my >= b.y && my <= b.y + b.height; } } @@ -916,7 +865,7 @@ static void update_panel_splitters(AppState *app) { Clay_ElementData d = Clay_GetElementData(CLAY_ID("SplitLog")); if (d.found) { Clay_BoundingBox b = d.boundingBox; - hover_log = mx >= b.x && mx <= b.x + b.width && + hover_log = mx >= b.x && mx <= b.x + b.width && my >= b.y - grab_margin && my <= b.y + b.height + grab_margin; } } @@ -924,35 +873,35 @@ static void update_panel_splitters(AppState *app) { // Start drag if (mouse_clicked && app->panel_drag == 0) { if (hover_browser) { - app->panel_drag = 1; + app->panel_drag = 1; app->panel_drag_start_mouse = input.mouse_pos.x; - app->panel_drag_start_size = app->browser_width; + app->panel_drag_start_size = app->browser_width; } else if (hover_right) { - app->panel_drag = 2; + app->panel_drag = 2; app->panel_drag_start_mouse = input.mouse_pos.x; - app->panel_drag_start_size = app->right_col_width; + app->panel_drag_start_size = app->right_col_width; } else if (hover_log) { - app->panel_drag = 3; + app->panel_drag = 3; app->panel_drag_start_mouse = input.mouse_pos.y; - app->panel_drag_start_size = app->log_height; + app->panel_drag_start_size = app->log_height; } } // During drag if (app->panel_drag == 1) { - F32 delta = (input.mouse_pos.x - app->panel_drag_start_mouse) / g_ui_scale; + F32 delta = (input.mouse_pos.x - app->panel_drag_start_mouse) / g_ui_scale; app->browser_width = Clamp(100.0f, app->panel_drag_start_size + delta, 500.0f); - cursor = PLATFORM_CURSOR_SIZE_WE; + cursor = PLATFORM_CURSOR_SIZE_WE; } else if (app->panel_drag == 2) { F32 delta = (input.mouse_pos.x - app->panel_drag_start_mouse) / g_ui_scale; // Right panel: dragging left increases width, right decreases app->right_col_width = Clamp(150.0f, app->panel_drag_start_size - delta, 500.0f); - cursor = PLATFORM_CURSOR_SIZE_WE; + cursor = PLATFORM_CURSOR_SIZE_WE; } else if (app->panel_drag == 3) { F32 delta = (input.mouse_pos.y - app->panel_drag_start_mouse) / g_ui_scale; // Log panel: dragging up increases height, down decreases app->log_height = Clamp(80.0f, app->panel_drag_start_size - delta, 500.0f); - cursor = PLATFORM_CURSOR_SIZE_NS; + cursor = PLATFORM_CURSOR_SIZE_NS; } // End drag @@ -973,149 +922,127 @@ static void update_panel_splitters(AppState *app) { // Header bar — transport / layout toggle static void build_header_bar(AppState *app) { - Clay_Color bar_bg = g_theme.header_bg; - Clay_Color border_bot = {(F32)Max((S32)bar_bg.r - 12, 0), (F32)Max((S32)bar_bg.g - 12, 0), (F32)Max((S32)bar_bg.b - 12, 0), 255}; + Clay_Color bar_bg = g_theme.header_bg; + Clay_Color border_bot = { (F32)Max((S32)bar_bg.r - 12, 0), (F32)Max((S32)bar_bg.g - 12, 0), (F32)Max((S32)bar_bg.b - 12, 0), 255 }; static Clay_TextElementConfig header_btn_active_text = {}; - header_btn_active_text.textColor = Clay_Color{255, 255, 255, 255}; - header_btn_active_text.fontSize = FONT_SIZE_NORMAL; - header_btn_active_text.wrapMode = CLAY_TEXT_WRAP_NONE; + header_btn_active_text.textColor = Clay_Color{ 255, 255, 255, 255 }; + header_btn_active_text.fontSize = FONT_SIZE_NORMAL; + header_btn_active_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig header_clock_text = {}; - header_clock_text.textColor = g_theme.text; - header_clock_text.fontSize = uifs(22); - header_clock_text.wrapMode = CLAY_TEXT_WRAP_NONE; + header_clock_text.textColor = g_theme.text; + header_clock_text.fontSize = uifs(22); + header_clock_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig header_indicator_text = {}; - header_indicator_text.textColor = g_theme.text; - header_indicator_text.fontSize = FONT_SIZE_NORMAL; - header_indicator_text.wrapMode = CLAY_TEXT_WRAP_NONE; + header_indicator_text.textColor = g_theme.text; + header_indicator_text.fontSize = FONT_SIZE_NORMAL; + header_indicator_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig header_indicator_label = {}; - header_indicator_label.textColor = g_theme.text_dim; - header_indicator_label.fontSize = FONT_SIZE_SMALL; - header_indicator_label.wrapMode = CLAY_TEXT_WRAP_NONE; + header_indicator_label.textColor = g_theme.text_dim; + header_indicator_label.fontSize = FONT_SIZE_SMALL; + header_indicator_label.wrapMode = CLAY_TEXT_WRAP_NONE; - Clay_Color inset_bg = {(F32)Max((S32)bar_bg.r - 8, 0), (F32)Max((S32)bar_bg.g - 8, 0), (F32)Max((S32)bar_bg.b - 8, 0), 255}; + Clay_Color inset_bg = { (F32)Max((S32)bar_bg.r - 8, 0), (F32)Max((S32)bar_bg.g - 8, 0), (F32)Max((S32)bar_bg.b - 8, 0), 255 }; CLAY(CLAY_ID("HeaderBar"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(64)) }, - .padding = { uip(10), uip(10), uip(6), uip(6) }, - .childGap = uip(8), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = bar_bg, - .border = { .color = border_bot, .width = { .bottom = 2 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(64)) }, + .padding = { uip(10), uip(10), uip(6), uip(6) }, + .childGap = uip(8), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = bar_bg, .border = { .color = border_bot, .width = { .bottom = 2 } }, ) { // === LEFT: toolbar buttons === CLAY(CLAY_ID("ToolbarGroup"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Rewind CLAY(CLAY_ID("TbRewind"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { ui_icon(UI_ICON_TRANSPORT_REWIND, uis(16), g_theme.text_dim); } + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(UI_ICON_TRANSPORT_REWIND, uis(16), g_theme.text_dim); } // Stop CLAY(CLAY_ID("TbStop"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { ui_icon(UI_ICON_TRANSPORT_STOP, uis(14), g_theme.text_dim); } + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(UI_ICON_TRANSPORT_STOP, uis(14), g_theme.text_dim); } // Play CLAY(CLAY_ID("TbPlay"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { ui_icon(UI_ICON_TRANSPORT_PLAY, uis(16), g_theme.text_dim); } + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(UI_ICON_TRANSPORT_PLAY, uis(16), g_theme.text_dim); } // Record CLAY(CLAY_ID("TbRecord"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { ui_icon(UI_ICON_TRANSPORT_RECORD, uis(14), Clay_Color{200, 60, 60, 255}); } + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(32)), .height = CLAY_SIZING_FIXED(uis(32)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(UI_ICON_TRANSPORT_RECORD, uis(14), Clay_Color{ 200, 60, 60, 255 }); } } // Spacer CLAY(CLAY_ID("HeaderSpacerL"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } } - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }) {} // === CENTER: time display (nearly full header height) === CLAY(CLAY_ID("TimeDisplay"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(200)), .height = CLAY_SIZING_FIXED(uis(48)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = inset_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(200)), .height = CLAY_SIZING_FIXED(uis(48)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = inset_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, ) { CLAY_TEXT(CLAY_STRING("00:00:00.000"), &header_clock_text); } // === Tempo & time sig indicators === CLAY(CLAY_ID("IndicatorGroup"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(10), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(10), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Tempo CLAY(CLAY_ID("TempoBox"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(48)) }, - .padding = { uip(10), uip(10), 0, 0 }, - .childGap = uip(2), - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = inset_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(48)) }, + .padding = { uip(10), uip(10), 0, 0 }, + .childGap = uip(2), + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = inset_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, ) { CLAY_TEXT(CLAY_STRING("TEMPO"), &header_indicator_label); CLAY_TEXT(CLAY_STRING("120.00"), &header_indicator_text); } // Time Signature CLAY(CLAY_ID("TimeSigBox"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(48)) }, - .padding = { uip(10), uip(10), 0, 0 }, - .childGap = uip(2), - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = inset_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(48)) }, + .padding = { uip(10), uip(10), 0, 0 }, + .childGap = uip(2), + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = inset_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .border = { .color = border_bot, .width = { .left = 1, .right = 1, .top = 1, .bottom = 1 } }, ) { CLAY_TEXT(CLAY_STRING("SIGNATURE"), &header_indicator_label); CLAY_TEXT(CLAY_STRING("4 / 4"), &header_indicator_text); } @@ -1123,26 +1050,23 @@ static void build_header_bar(AppState *app) { // Spacer CLAY(CLAY_ID("HeaderSpacerR"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } } - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }) {} // === RIGHT: Edit / Mix buttons === // Edit button { - Clay_ElementId edit_eid = CLAY_ID("BtnLayoutEdit"); - B32 edit_hovered = Clay_PointerOver(edit_eid); - B32 edit_active = (app->master_layout == 0); - Clay_Color edit_bg = edit_active ? g_theme.accent : g_theme.bg_lighter; + Clay_ElementId edit_eid = CLAY_ID("BtnLayoutEdit"); + B32 edit_hovered = Clay_PointerOver(edit_eid); + B32 edit_active = (app->master_layout == 0); + Clay_Color edit_bg = edit_active ? g_theme.accent : g_theme.bg_lighter; if (edit_hovered && !edit_active) edit_bg = g_theme.accent_hover; CLAY(edit_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, - .padding = { uip(14), uip(14), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = edit_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, + .padding = { uip(14), uip(14), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = edit_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { CLAY_TEXT(CLAY_STRING("Edit"), edit_active ? &header_btn_active_text : &g_text_config_normal); } if (edit_hovered && g_wstate.mouse_clicked) { @@ -1152,20 +1076,18 @@ static void build_header_bar(AppState *app) { // Mix button { - Clay_ElementId mix_eid = CLAY_ID("BtnLayoutMix"); - B32 mix_hovered = Clay_PointerOver(mix_eid); - B32 mix_active = (app->master_layout == 1); - Clay_Color mix_bg = mix_active ? g_theme.accent : g_theme.bg_lighter; + Clay_ElementId mix_eid = CLAY_ID("BtnLayoutMix"); + B32 mix_hovered = Clay_PointerOver(mix_eid); + B32 mix_active = (app->master_layout == 1); + Clay_Color mix_bg = mix_active ? g_theme.accent : g_theme.bg_lighter; if (mix_hovered && !mix_active) mix_bg = g_theme.accent_hover; CLAY(mix_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, - .padding = { uip(14), uip(14), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = mix_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, + .padding = { uip(14), uip(14), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = mix_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { CLAY_TEXT(CLAY_STRING("Mix"), mix_active ? &header_btn_active_text : &g_text_config_normal); } if (mix_hovered && g_wstate.mouse_clicked) { @@ -1180,17 +1102,15 @@ static void build_header_bar(AppState *app) { // Mix pop-out / pop-in button { - B32 mix_popped = app->show_mix_popout; - Clay_ElementId pop_eid = CLAY_ID("BtnMixPopOut"); - B32 pop_hovered = Clay_PointerOver(pop_eid); + B32 mix_popped = app->show_mix_popout; + Clay_ElementId pop_eid = CLAY_ID("BtnMixPopOut"); + B32 pop_hovered = Clay_PointerOver(pop_eid); CLAY(pop_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(22)), .height = CLAY_SIZING_FIXED(uis(22)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = pop_hovered ? g_theme.accent_hover : g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(22)), .height = CLAY_SIZING_FIXED(uis(22)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = pop_hovered ? g_theme.accent_hover : g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(mix_popped ? UI_ICON_POP_IN : UI_ICON_POP_OUT, uis(12), g_theme.text_dim); } if (pop_hovered && g_wstate.mouse_clicked) { @@ -1207,20 +1127,18 @@ static void build_header_bar(AppState *app) { // Patch button { - Clay_ElementId patch_eid = CLAY_ID("BtnLayoutPatch"); - B32 patch_hovered = Clay_PointerOver(patch_eid); - B32 patch_active = (app->master_layout == 2); - Clay_Color patch_bg = patch_active ? g_theme.accent : g_theme.bg_lighter; + Clay_ElementId patch_eid = CLAY_ID("BtnLayoutPatch"); + B32 patch_hovered = Clay_PointerOver(patch_eid); + B32 patch_active = (app->master_layout == 2); + Clay_Color patch_bg = patch_active ? g_theme.accent : g_theme.bg_lighter; if (patch_hovered && !patch_active) patch_bg = g_theme.accent_hover; CLAY(patch_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, - .padding = { uip(14), uip(14), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = patch_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(28)) }, + .padding = { uip(14), uip(14), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = patch_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { CLAY_TEXT(CLAY_STRING("Patch"), patch_active ? &header_btn_active_text : &g_text_config_normal); } if (patch_hovered && g_wstate.mouse_clicked) { @@ -1235,17 +1153,15 @@ static void build_header_bar(AppState *app) { // Patch pop-out / pop-in button { - B32 patch_popped = app->show_patch_popout; - Clay_ElementId pop_eid = CLAY_ID("BtnPatchPopOut"); - B32 pop_hovered = Clay_PointerOver(pop_eid); + B32 patch_popped = app->show_patch_popout; + Clay_ElementId pop_eid = CLAY_ID("BtnPatchPopOut"); + B32 pop_hovered = Clay_PointerOver(pop_eid); CLAY(pop_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(22)), .height = CLAY_SIZING_FIXED(uis(22)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = pop_hovered ? g_theme.accent_hover : g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(22)), .height = CLAY_SIZING_FIXED(uis(22)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = pop_hovered ? g_theme.accent_hover : g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { ui_icon(patch_popped ? UI_ICON_POP_IN : UI_ICON_POP_OUT, uis(12), g_theme.text_dim); } if (pop_hovered && g_wstate.mouse_clicked) { @@ -1266,20 +1182,18 @@ static void build_header_bar(AppState *app) { // Mix view — channel strip faders static void build_mix_view(AppState *app) { - Clay_Color mv_top = g_theme.bg_medium; - Clay_Color mv_bot = {(F32)Max((S32)mv_top.r - 8, 0), (F32)Max((S32)mv_top.g - 8, 0), (F32)Max((S32)mv_top.b - 8, 0), 255}; + Clay_Color mv_top = g_theme.bg_medium; + Clay_Color mv_bot = { (F32)Max((S32)mv_top.r - 8, 0), (F32)Max((S32)mv_top.g - 8, 0), (F32)Max((S32)mv_top.b - 8, 0), 255 }; CustomGradientData *mv_grad = alloc_gradient(mv_top, mv_bot); CLAY(CLAY_ID("MixView"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(4), uip(4), uip(4), uip(4) }, - .childGap = 0, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = mv_top, - .custom = { .customData = mv_grad }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(4), uip(4), uip(4), uip(4) }, + .childGap = 0, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = mv_top, .custom = { .customData = mv_grad }, ) { static char ch_label_bufs[8][8]; static char fader_id_bufs[8][16]; static char pan_id_bufs[8][16]; @@ -1292,39 +1206,34 @@ static void build_mix_view(AppState *app) { Clay_Color strip_bg = g_theme.bg_medium; CLAY(CLAY_IDI("MixStrip", i), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(8), uip(8) }, - .childGap = uip(8), - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = strip_bg, - .border = { .color = g_theme.border, .width = { .right = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(8), uip(8) }, + .childGap = uip(8), + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = strip_bg, .border = { .color = g_theme.border, .width = { .right = 1 } }, ) { // Channel label - S32 llen = snprintf(ch_label_bufs[i], sizeof(ch_label_bufs[i]), "Ch %d", i + 1); + S32 llen = snprintf(ch_label_bufs[i], sizeof(ch_label_bufs[i]), "Ch %d", i + 1); Clay_String ch_str = { .isStaticallyAllocated = false, .length = llen, .chars = ch_label_bufs[i] }; CLAY_TEXT(ch_str, &g_text_config_dim); // SEND/RECV button - Clay_ElementId sr_eid = CLAY_IDI("MixSendRecv", i); - B32 sr_hovered = Clay_PointerOver(sr_eid); + Clay_ElementId sr_eid = CLAY_IDI("MixSendRecv", i); + B32 sr_hovered = Clay_PointerOver(sr_eid); CLAY(sr_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(22)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = sr_hovered ? g_theme.accent_hover : g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(22)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = sr_hovered ? g_theme.accent_hover : g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { CLAY_TEXT(CLAY_STRING("SEND/RECV"), &g_text_config_dim); } // Spacer pushes pan + fader to bottom CLAY(CLAY_IDI("MixSpacer", i), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() } } - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() } }) {} // Pan knob ui_knob(pan_id_bufs[i], "Pan", &app->mix_pans[i], 50.0f, 1, 0.0f, 1); @@ -1345,36 +1254,32 @@ static void build_mix_view(AppState *app) { static F32 master_fader = 0.0f; CLAY(CLAY_ID("MixMaster"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(0, uis(120)), .height = CLAY_SIZING_GROW() }, - .padding = { uip(8), uip(8), uip(8), uip(8) }, - .childGap = uip(8), - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = master_bg, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(0, uis(120)), .height = CLAY_SIZING_GROW() }, + .padding = { uip(8), uip(8), uip(8), uip(8) }, + .childGap = uip(8), + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = master_bg, ) { CLAY_TEXT(CLAY_STRING("Master"), &g_text_config_normal); // SEND/RECV button { - Clay_ElementId msr_eid = CLAY_ID("MixMasterSendRecv"); - B32 msr_hovered = Clay_PointerOver(msr_eid); + Clay_ElementId msr_eid = CLAY_ID("MixMasterSendRecv"); + B32 msr_hovered = Clay_PointerOver(msr_eid); CLAY(msr_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(22)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = msr_hovered ? g_theme.accent_hover : g_theme.bg_lighter, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(uis(22)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = msr_hovered ? g_theme.accent_hover : g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), ) { CLAY_TEXT(CLAY_STRING("SEND/RECV"), &g_text_config_dim); } } CLAY(CLAY_ID("MixMasterSpacer"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() } } - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() } }) {} // Master pan knob ui_knob("MixMasterPan", "Pan", &app->mix_master_pan, 50.0f, 1, 0.0f, 1); @@ -1389,109 +1294,101 @@ static void build_mix_view(AppState *app) { // Patch view — routing matrix / graph static void build_patch_view(AppState *app) { - Clay_Color pv_top = g_theme.bg_medium; - Clay_Color pv_bot = {(F32)Max((S32)pv_top.r - 8, 0), (F32)Max((S32)pv_top.g - 8, 0), (F32)Max((S32)pv_top.b - 8, 0), 255}; + Clay_Color pv_top = g_theme.bg_medium; + Clay_Color pv_bot = { (F32)Max((S32)pv_top.r - 8, 0), (F32)Max((S32)pv_top.g - 8, 0), (F32)Max((S32)pv_top.b - 8, 0), 255 }; CustomGradientData *pv_grad = alloc_gradient(pv_top, pv_bot); CLAY(CLAY_ID("PatchView"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = pv_top, - .custom = { .customData = pv_grad }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = pv_top, .custom = { .customData = pv_grad }, ) { static const char *patch_tabs[] = { "Matrix", "Graph" }; ui_tab_bar("PatchTabs", patch_tabs, 2, &app->patch_tab); if (app->patch_tab == 0) { - // === Matrix routing view === - #define MATRIX_INPUTS 32 - #define MATRIX_OUTPUTS 33 // Master + Out 1..32 - #define HW_OUTPUTS 32 +// === Matrix routing view === +#define MATRIX_INPUTS 32 +#define MATRIX_OUTPUTS 33 // Master + Out 1..32 +#define HW_OUTPUTS 32 static Clay_TextElementConfig matrix_hdr_text = {}; - matrix_hdr_text.textColor = g_theme.text_dim; - matrix_hdr_text.fontSize = FONT_SIZE_SMALL; - matrix_hdr_text.wrapMode = CLAY_TEXT_WRAP_NONE; + matrix_hdr_text.textColor = g_theme.text_dim; + matrix_hdr_text.fontSize = FONT_SIZE_SMALL; + matrix_hdr_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig matrix_axis_text = {}; - matrix_axis_text.textColor = g_theme.text; - matrix_axis_text.fontSize = FONT_SIZE_SMALL; - matrix_axis_text.wrapMode = CLAY_TEXT_WRAP_NONE; + matrix_axis_text.textColor = g_theme.text; + matrix_axis_text.fontSize = FONT_SIZE_SMALL; + matrix_axis_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig matrix_title_text = {}; - matrix_title_text.textColor = g_theme.text; - matrix_title_text.fontSize = FONT_SIZE_NORMAL; - matrix_title_text.wrapMode = CLAY_TEXT_WRAP_NONE; + matrix_title_text.textColor = g_theme.text; + matrix_title_text.fontSize = FONT_SIZE_NORMAL; + matrix_title_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig cell_x_text = {}; - cell_x_text.textColor = Clay_Color{255, 255, 255, 255}; - cell_x_text.fontSize = FONT_SIZE_SMALL; - cell_x_text.wrapMode = CLAY_TEXT_WRAP_NONE; + cell_x_text.textColor = Clay_Color{ 255, 255, 255, 255 }; + cell_x_text.fontSize = FONT_SIZE_SMALL; + cell_x_text.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig fb_text = {}; - fb_text.textColor = Clay_Color{80, 80, 80, 255}; - fb_text.fontSize = FONT_SIZE_SMALL; - fb_text.wrapMode = CLAY_TEXT_WRAP_NONE; + fb_text.textColor = Clay_Color{ 80, 80, 80, 255 }; + fb_text.fontSize = FONT_SIZE_SMALL; + fb_text.wrapMode = CLAY_TEXT_WRAP_NONE; F32 cell_size = uis(20); - F32 label_w = uis(48); + F32 label_w = uis(48); // Scrollable container for both matrices side by side CLAY(CLAY_ID("MatrixScroll"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(12), uip(12), uip(8), uip(8) }, - .childGap = uip(32), - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(12), uip(12), uip(8), uip(8) }, + .childGap = uip(32), + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .clip = { .horizontal = true, .vertical = true, .childOffset = Clay_GetScrollOffset() }, ) { // ============================================================ // INTERNAL ROUTING matrix (32 inputs x 33 outputs) // ============================================================ CLAY(CLAY_ID("InternalMatrix"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Title CLAY_TEXT(CLAY_STRING("Internal Routing"), &matrix_title_text); // Axis label: "OUTPUT >" CLAY(CLAY_ID("IntOutputLabel"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .padding = { (U16)label_w, 0, 0, 0 }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .padding = { (U16)label_w, 0, 0, 0 }, + }) { CLAY_TEXT(CLAY_STRING("OUTPUT >"), &matrix_axis_text); } // Grid CLAY(CLAY_ID("IntGrid"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Column headers CLAY(CLAY_ID("IntHeaderRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { CLAY(CLAY_ID("IntCorner"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, - .padding = { 0, uip(4), 0, 0 }, - .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, + .padding = { 0, uip(4), 0, 0 }, + .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, + }) { CLAY_TEXT(CLAY_STRING("INPUT v"), &matrix_axis_text); } @@ -1505,11 +1402,10 @@ static void build_patch_view(AppState *app) { Clay_String dst_str = { .isStaticallyAllocated = false, .length = dlen, .chars = int_dst_bufs[d] }; CLAY(CLAY_IDI("IntDstHdr", d), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, ) { CLAY_TEXT(dst_str, d == 0 ? &matrix_axis_text : &matrix_hdr_text); } } @@ -1518,29 +1414,27 @@ static void build_patch_view(AppState *app) { // Rows static char int_src_bufs[MATRIX_INPUTS][8]; for (S32 s = 0; s < MATRIX_INPUTS; s++) { - S32 slen = snprintf(int_src_bufs[s], sizeof(int_src_bufs[s]), "Ch %d", s + 1); + S32 slen = snprintf(int_src_bufs[s], sizeof(int_src_bufs[s]), "Ch %d", s + 1); Clay_String src_str = { .isStaticallyAllocated = false, .length = slen, .chars = int_src_bufs[s] }; CLAY(CLAY_IDI("IntRow", s), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { CLAY(CLAY_IDI("IntSrcLbl", s), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, - .padding = { 0, uip(4), 0, 0 }, - .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, - }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, + .padding = { 0, uip(4), 0, 0 }, + .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, + }, ) { CLAY_TEXT(src_str, &matrix_hdr_text); } for (S32 d = 0; d < MATRIX_OUTPUTS; d++) { - S32 cell_idx = s * MATRIX_OUTPUTS + d; - Clay_ElementId cell_eid = CLAY_IDI("IntCell", cell_idx); - B32 cell_hovered = Clay_PointerOver(cell_eid); + S32 cell_idx = s * MATRIX_OUTPUTS + d; + Clay_ElementId cell_eid = CLAY_IDI("IntCell", cell_idx); + B32 cell_hovered = Clay_PointerOver(cell_eid); B32 is_feedback = (d >= 1 && d == s + 1); if (is_feedback) app->patch_matrix[s][d] = 0; @@ -1577,13 +1471,11 @@ static void build_patch_view(AppState *app) { } CLAY(cell_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = cell_bg, - .border = { .color = g_theme.border, .width = { .right = 1, .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = cell_bg, .border = { .color = g_theme.border, .width = { .right = 1, .bottom = 1 } }, ) { if (is_feedback) { CLAY_TEXT(CLAY_STRING("-"), &fb_text); } else if (active) { @@ -1604,60 +1496,54 @@ static void build_patch_view(AppState *app) { // HARDWARE ROUTING matrix (32 ch outputs x 32 hw outputs) // ============================================================ CLAY(CLAY_ID("HardwareMatrix"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Title CLAY_TEXT(CLAY_STRING("Hardware Routing"), &matrix_title_text); // Axis label: "HW OUTPUT >" CLAY(CLAY_ID("HwOutputLabel"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .padding = { (U16)label_w, 0, 0, 0 }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .padding = { (U16)label_w, 0, 0, 0 }, + }) { CLAY_TEXT(CLAY_STRING("HW OUTPUT >"), &matrix_axis_text); } // Grid CLAY(CLAY_ID("HwGrid"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Column headers CLAY(CLAY_ID("HwHeaderRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { CLAY(CLAY_ID("HwCorner"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, - .padding = { 0, uip(4), 0, 0 }, - .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, + .padding = { 0, uip(4), 0, 0 }, + .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, + }) { CLAY_TEXT(CLAY_STRING("CH OUT v"), &matrix_axis_text); } static char hw_dst_bufs[HW_OUTPUTS][8]; for (S32 d = 0; d < HW_OUTPUTS; d++) { - S32 dlen = snprintf(hw_dst_bufs[d], sizeof(hw_dst_bufs[d]), "%d", d + 1); + S32 dlen = snprintf(hw_dst_bufs[d], sizeof(hw_dst_bufs[d]), "%d", d + 1); Clay_String dst_str = { .isStaticallyAllocated = false, .length = dlen, .chars = hw_dst_bufs[d] }; CLAY(CLAY_IDI("HwDstHdr", d), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, ) { CLAY_TEXT(dst_str, &matrix_hdr_text); } } @@ -1666,30 +1552,28 @@ static void build_patch_view(AppState *app) { // Rows static char hw_src_bufs[HW_OUTPUTS][8]; for (S32 s = 0; s < HW_OUTPUTS; s++) { - S32 slen = snprintf(hw_src_bufs[s], sizeof(hw_src_bufs[s]), "Out %d", s + 1); + S32 slen = snprintf(hw_src_bufs[s], sizeof(hw_src_bufs[s]), "Out %d", s + 1); Clay_String src_str = { .isStaticallyAllocated = false, .length = slen, .chars = hw_src_bufs[s] }; CLAY(CLAY_IDI("HwRow", s), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(cell_size) }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { CLAY(CLAY_IDI("HwSrcLbl", s), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, - .padding = { 0, uip(4), 0, 0 }, - .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, - }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(label_w), .height = CLAY_SIZING_FIXED(cell_size) }, + .padding = { 0, uip(4), 0, 0 }, + .childAlignment = { .x = CLAY_ALIGN_X_RIGHT, .y = CLAY_ALIGN_Y_CENTER }, + }, ) { CLAY_TEXT(src_str, &matrix_hdr_text); } for (S32 d = 0; d < HW_OUTPUTS; d++) { - S32 cell_idx = s * HW_OUTPUTS + d; - Clay_ElementId cell_eid = CLAY_IDI("HwCell", cell_idx); - B32 cell_hovered = Clay_PointerOver(cell_eid); - B32 active = app->hw_matrix[s][d]; + S32 cell_idx = s * HW_OUTPUTS + d; + Clay_ElementId cell_eid = CLAY_IDI("HwCell", cell_idx); + B32 cell_hovered = Clay_PointerOver(cell_eid); + B32 active = app->hw_matrix[s][d]; Clay_Color cell_bg; if (active) { @@ -1707,13 +1591,11 @@ static void build_patch_view(AppState *app) { } CLAY(cell_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = cell_bg, - .border = { .color = g_theme.border, .width = { .right = 1, .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cell_size), .height = CLAY_SIZING_FIXED(cell_size) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = cell_bg, .border = { .color = g_theme.border, .width = { .right = 1, .bottom = 1 } }, ) { if (active) { CLAY_TEXT(CLAY_STRING("X"), &cell_x_text); } @@ -1731,13 +1613,12 @@ static void build_patch_view(AppState *app) { } else { // === Graph view (placeholder) === CLAY(CLAY_ID("GraphContent"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(16), uip(16), uip(12), uip(12) }, - .childGap = 0, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(16), uip(16), uip(12), uip(12) }, + .childGap = 0, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }) { CLAY_TEXT(CLAY_STRING("Graph view coming soon"), &g_text_config_dim); } } @@ -1760,29 +1641,26 @@ static void patch_popout_content(void *user_data) { static void build_ui(AppState *app) { CLAY(CLAY_ID("Root"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { build_header_bar(app); if (app->master_layout == 0) { // === EDIT MODE (existing layout) === CLAY(CLAY_ID("TopRow"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { build_browser_panel(app); // Browser splitter (vertical, 1px line) if (app->show_browser) { CLAY(CLAY_ID("SplitBrowser"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(1), .height = CLAY_SIZING_GROW() } }, - .backgroundColor = g_theme.border - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(1), .height = CLAY_SIZING_GROW() } }, + .backgroundColor = g_theme.border) {} } build_main_panel(app); @@ -1790,16 +1668,14 @@ static void build_ui(AppState *app) { if (app->show_props || app->show_midi_devices) { // Right splitter (vertical, 1px line) CLAY(CLAY_ID("SplitRight"), - .layout = { .sizing = { .width = CLAY_SIZING_FIXED(1), .height = CLAY_SIZING_GROW() } }, - .backgroundColor = g_theme.border - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(1), .height = CLAY_SIZING_GROW() } }, + .backgroundColor = g_theme.border) {} CLAY(CLAY_ID("RightColumn"), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(app->right_col_width)), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(app->right_col_width)), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, ) { build_right_panel(app); } } @@ -1808,9 +1684,8 @@ static void build_ui(AppState *app) { // Log splitter (horizontal, 1px line) if (app->show_log) { CLAY(CLAY_ID("SplitLog"), - .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, - .backgroundColor = g_theme.border - ) {} + .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(1) } }, + .backgroundColor = g_theme.border) {} } build_log_panel(app); @@ -1826,7 +1701,6 @@ static void build_ui(AppState *app) { } } } - } //////////////////////////////// @@ -1836,13 +1710,13 @@ static void build_ui(AppState *app) { static void do_frame(AppState *app) { // Timing #ifdef __APPLE__ - U64 now = mach_absolute_time(); - F32 dt = (F32)(now - app->last_time) * (F32)app->freq_numer / ((F32)app->freq_denom * 1e9f); + U64 now = mach_absolute_time(); + F32 dt = (F32)(now - app->last_time) * (F32)app->freq_numer / ((F32)app->freq_denom * 1e9f); app->last_time = now; #else LARGE_INTEGER now; QueryPerformanceCounter(&now); - F32 dt = (F32)(now.QuadPart - app->last_time.QuadPart) / (F32)app->freq.QuadPart; + F32 dt = (F32)(now.QuadPart - app->last_time.QuadPart) / (F32)app->freq.QuadPart; app->last_time = now; #endif if (dt > 0.1f) dt = 0.1f; @@ -1874,19 +1748,19 @@ static void do_frame(AppState *app) { if (input.ctrl_held) { if (input.keys[k] == PKEY_EQUAL) app->ui_scale *= 1.1f; if (input.keys[k] == PKEY_MINUS) app->ui_scale /= 1.1f; - if (input.keys[k] == PKEY_0) app->ui_scale = 1.0f; + if (input.keys[k] == PKEY_0) app->ui_scale = 1.0f; } } app->ui_scale = Clamp(0.5f, app->ui_scale, 3.0f); - g_ui_scale = app->ui_scale; + g_ui_scale = app->ui_scale; F32 dpi_scale = platform_get_dpi_scale(app->window); renderer_set_font_scale(app->renderer, app->ui_scale * dpi_scale); // Handle theme change if (app->settings_theme_sel != app->settings_theme_prev) { ui_set_theme(app->settings_theme_sel); - ui_set_accent(app->accent_sel); // reapply accent on new base theme - g_theme.corner_radius = radius_values[app->radius_sel]; // preserve user radius + ui_set_accent(app->accent_sel); // reapply accent on new base theme + g_theme.corner_radius = radius_values[app->radius_sel]; // preserve user radius app->settings_theme_prev = app->settings_theme_sel; // Refresh all text configs with new theme colors @@ -1897,7 +1771,7 @@ static void do_frame(AppState *app) { // Update renderer clear color to match theme renderer_set_clear_color(app->renderer, - g_theme.bg_dark.r / 255.f, g_theme.bg_dark.g / 255.f, g_theme.bg_dark.b / 255.f); + g_theme.bg_dark.r / 255.f, g_theme.bg_dark.g / 255.f, g_theme.bg_dark.b / 255.f); } // Handle accent change @@ -1951,24 +1825,24 @@ int main(int argc, char **argv) { (void)argv; PlatformWindowDesc window_desc = {}; - PlatformWindow *window = platform_create_window(&window_desc); + PlatformWindow *window = platform_create_window(&window_desc); if (!window) return 1; S32 w, h; platform_get_size(window, &w, &h); - RendererDesc renderer_desc = {}; + RendererDesc renderer_desc = {}; renderer_desc.window_handle = platform_get_native_handle(window); renderer_desc.width = w; renderer_desc.height = h; - Renderer *renderer = renderer_create(&renderer_desc); + Renderer *renderer = renderer_create(&renderer_desc); if (!renderer) { platform_destroy_window(window); return 1; } - MidiEngine *midi = midi_create(); + MidiEngine *midi = midi_create(); AudioEngine *audio = audio_create(platform_get_native_handle(window)); // Initialize UI (Clay) @@ -1990,42 +1864,50 @@ int main(int argc, char **argv) { } } - AppState app = {}; - app.window = window; - app.renderer = renderer; - app.midi = midi; - app.audio = audio; - app.ui = ui; - app.last_w = w; - app.last_h = h; - app.ui_scale = 1.0f; - app.show_browser = 1; - app.show_props = 1; - app.show_log = 1; - app.show_midi_devices = 1; - app.bottom_panel_tab = 0; + AppState app = {}; + app.window = window; + app.renderer = renderer; + app.midi = midi; + app.audio = audio; + app.ui = ui; + app.last_w = w; + app.last_h = h; + app.ui_scale = 1.0f; + app.show_browser = 1; + app.show_props = 1; + app.show_log = 1; + app.show_midi_devices = 1; + app.bottom_panel_tab = 0; app.piano_state.mouse_note = -1; - app.demo_knob_unsigned = 75.0f; - app.demo_knob_signed = 0.0f; - app.demo_slider_h = 50.0f; - app.demo_slider_v = 75.0f; - app.demo_fader = 0.0f; - app.demo_dropdown_sel = 1; // default to 48000 Hz - app.radius_sel = 1; // default to "Small" (4.0f) - app.browser_width = 200.0f; - app.right_col_width = 250.0f; - app.log_height = 180.0f; - app.panel_drag = 0; - app.master_layout = 0; - for (S32 i = 0; i < 8; i++) { app.mix_faders[i] = 0.0f; app.mix_pans[i] = 0.0f; } + app.demo_knob_unsigned = 75.0f; + app.demo_knob_signed = 0.0f; + app.demo_slider_h = 50.0f; + app.demo_slider_v = 75.0f; + app.demo_fader = 0.0f; + app.demo_dropdown_sel = 1; // default to 48000 Hz + app.radius_sel = 1; // default to "Small" (4.0f) + app.browser_width = 200.0f; + app.right_col_width = 250.0f; + app.log_height = 180.0f; + app.panel_drag = 0; + app.master_layout = 0; + for (S32 i = 0; i < 8; i++) { + app.mix_faders[i] = 0.0f; + app.mix_pans[i] = 0.0f; + } app.mix_master_pan = 0.0f; - app.patch_tab = 0; + app.patch_tab = 0; memset(app.patch_matrix, 0, sizeof(app.patch_matrix)); memset(app.hw_matrix, 0, sizeof(app.hw_matrix)); snprintf(app.demo_text_a, sizeof(app.demo_text_a), "My Instrument"); #ifdef __APPLE__ snprintf(app.demo_text_b, sizeof(app.demo_text_b), "~/Samples/output"); - { mach_timebase_info_data_t tbi; mach_timebase_info(&tbi); app.freq_numer = tbi.numer; app.freq_denom = tbi.denom; } + { + mach_timebase_info_data_t tbi; + mach_timebase_info(&tbi); + app.freq_numer = tbi.numer; + app.freq_denom = tbi.denom; + } app.last_time = mach_absolute_time(); #else snprintf(app.demo_text_b, sizeof(app.demo_text_b), "C:\\Samples\\output"); @@ -2040,20 +1922,20 @@ int main(int argc, char **argv) { // Menu commands S32 menu_cmd = platform_poll_menu_command(window); switch (menu_cmd) { - case MENU_FILE_EXIT: running = 0; break; - case MENU_VIEW_BROWSER: app.show_browser = !app.show_browser; break; - case MENU_VIEW_PROPERTIES:app.show_props = !app.show_props; break; - case MENU_VIEW_LOG: app.show_log = !app.show_log; break; + case MENU_FILE_EXIT: running = 0; break; + case MENU_VIEW_BROWSER: app.show_browser = !app.show_browser; break; + case MENU_VIEW_PROPERTIES: app.show_props = !app.show_props; break; + case MENU_VIEW_LOG: app.show_log = !app.show_log; break; case MENU_VIEW_MIDI_DEVICES: app.show_midi_devices = !app.show_midi_devices; break; - case MENU_PREFERENCES_EDIT: app.show_settings_window = 1; break; + case MENU_PREFERENCES_EDIT: app.show_settings_window = 1; break; default: break; } // Native confirmation dialog (blocking) if (app.show_confirm_dialog) { - S32 result = platform_message_box(window, "Confirm Action", - "Are you sure you want to proceed? This action cannot be undone.", - PLATFORM_MSGBOX_OK_CANCEL); + S32 result = platform_message_box(window, "Confirm Action", + "Are you sure you want to proceed? This action cannot be undone.", + PLATFORM_MSGBOX_OK_CANCEL); app.show_confirm_dialog = 0; (void)result; // result: 0 = OK, 1 = Cancel } diff --git a/src/menus.cpp b/src/menus.cpp index 0c7e53e..3cdeb2c 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -19,25 +19,25 @@ enum MenuCmd { static void setup_menus(PlatformWindow *window) { PlatformMenuItem file_items[] = { - { "New", MENU_FILE_NEW }, - { "Open...", MENU_FILE_OPEN }, - { "Save", MENU_FILE_SAVE }, + { "New", MENU_FILE_NEW }, + { "Open...", MENU_FILE_OPEN }, + { "Save", MENU_FILE_SAVE }, { "Save As...", MENU_FILE_SAVE_AS }, - { nullptr, 0 }, - { "Exit", MENU_FILE_EXIT }, + { nullptr, 0 }, + { "Exit", MENU_FILE_EXIT }, }; PlatformMenuItem import_items[] = { - { "Audio...", MENU_IMPORT_AUDIO }, - { "MIDI...", MENU_IMPORT_MIDI }, + { "Audio...", MENU_IMPORT_AUDIO }, + { "MIDI...", MENU_IMPORT_MIDI }, }; PlatformMenuItem view_items[] = { - { "Browser", MENU_VIEW_BROWSER }, + { "Browser", MENU_VIEW_BROWSER }, { "Properties", MENU_VIEW_PROPERTIES }, - { "Log", MENU_VIEW_LOG }, - { nullptr, 0 }, - { "Demo", MENU_VIEW_DEMO }, + { "Log", MENU_VIEW_LOG }, + { nullptr, 0 }, + { "Demo", MENU_VIEW_DEMO }, { "MIDI Devices", MENU_VIEW_MIDI_DEVICES }, }; @@ -46,10 +46,10 @@ static void setup_menus(PlatformWindow *window) { }; PlatformMenu menus[] = { - { "File", file_items, sizeof(file_items) / sizeof(file_items[0]) }, - { "Import", import_items, sizeof(import_items) / sizeof(import_items[0]) }, - { "View", view_items, sizeof(view_items) / sizeof(view_items[0]) }, - { "Preferences", prefs_items, sizeof(prefs_items) / sizeof(prefs_items[0]) }, + { "File", file_items, sizeof(file_items) / sizeof(file_items[0]) }, + { "Import", import_items, sizeof(import_items) / sizeof(import_items[0]) }, + { "View", view_items, sizeof(view_items) / sizeof(view_items[0]) }, + { "Preferences", prefs_items, sizeof(prefs_items) / sizeof(prefs_items[0]) }, }; platform_set_menu(window, menus, sizeof(menus) / sizeof(menus[0])); diff --git a/src/midi/midi.h b/src/midi/midi.h index 515a121..520ce29 100644 --- a/src/midi/midi.h +++ b/src/midi/midi.h @@ -5,27 +5,27 @@ struct MidiEngine; struct MidiDeviceInfo { - char name[64]; - S32 id; - B32 is_input; - B32 active; // true when note(s) currently held - B32 releasing; // true during release flash - S32 velocity; // last note-on velocity (0-127) - S32 note; // last MIDI note number (0-127) + char name[64]; + S32 id; + B32 is_input; + B32 active; // true when note(s) currently held + B32 releasing; // true during release flash + S32 velocity; // last note-on velocity (0-127) + S32 note; // last MIDI note number (0-127) }; MidiEngine *midi_create(); void midi_destroy(MidiEngine *engine); void midi_refresh_devices(MidiEngine *engine); -S32 midi_get_device_count(MidiEngine *engine); +S32 midi_get_device_count(MidiEngine *engine); MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index); -void midi_open_all_inputs(MidiEngine *engine); -void midi_close_all_inputs(MidiEngine *engine); -void midi_update(MidiEngine *engine, F32 dt); -B32 midi_is_input_active(MidiEngine *engine, S32 device_index); +void midi_open_all_inputs(MidiEngine *engine); +void midi_close_all_inputs(MidiEngine *engine); +void midi_update(MidiEngine *engine, F32 dt); +B32 midi_is_input_active(MidiEngine *engine, S32 device_index); // Per-note state: returns true if note (0-127) is currently held on any input device -B32 midi_is_note_held(MidiEngine *engine, S32 note); +B32 midi_is_note_held(MidiEngine *engine, S32 note); // Returns the last note-on velocity (0-127) for a given note, or 0 if not held -S32 midi_get_note_velocity(MidiEngine *engine, S32 note); +S32 midi_get_note_velocity(MidiEngine *engine, S32 note); diff --git a/src/midi/midi_coremidi.cpp b/src/midi/midi_coremidi.cpp index 4e840a4..25fc464 100644 --- a/src/midi/midi_coremidi.cpp +++ b/src/midi/midi_coremidi.cpp @@ -1,36 +1,36 @@ #include "midi/midi.h" -#include #include -#include +#include #include +#include -#define MIDI_MAX_DEVICES 64 +#define MIDI_MAX_DEVICES 64 #define MIDI_RELEASE_FLASH_DURATION 0.15f struct MidiEngine { - MidiDeviceInfo devices[MIDI_MAX_DEVICES]; - S32 device_count; + MidiDeviceInfo devices[MIDI_MAX_DEVICES]; + S32 device_count; - MIDIClientRef client; - MIDIPortRef input_port; + MIDIClientRef client; + MIDIPortRef input_port; // Map: source endpoint index -> our device array index - S32 source_to_device[MIDI_MAX_DEVICES]; + S32 source_to_device[MIDI_MAX_DEVICES]; // Set atomically from callback thread - _Atomic S32 pending_note_on_vel[MIDI_MAX_DEVICES]; - _Atomic S32 pending_note_num[MIDI_MAX_DEVICES]; - _Atomic S32 pending_note_off[MIDI_MAX_DEVICES]; - _Atomic S32 held_note_count[MIDI_MAX_DEVICES]; + _Atomic S32 pending_note_on_vel[MIDI_MAX_DEVICES]; + _Atomic S32 pending_note_num[MIDI_MAX_DEVICES]; + _Atomic S32 pending_note_off[MIDI_MAX_DEVICES]; + _Atomic S32 held_note_count[MIDI_MAX_DEVICES]; // Per-note state (across all devices), set atomically from callback - _Atomic S32 note_states[128]; // held count - _Atomic S32 note_velocities[128]; // last note-on velocity + _Atomic S32 note_states[128]; // held count + _Atomic S32 note_velocities[128]; // last note-on velocity // Main thread only - S32 display_velocity[MIDI_MAX_DEVICES]; - S32 display_note[MIDI_MAX_DEVICES]; - F32 release_timers[MIDI_MAX_DEVICES]; + S32 display_velocity[MIDI_MAX_DEVICES]; + S32 display_note[MIDI_MAX_DEVICES]; + F32 release_timers[MIDI_MAX_DEVICES]; }; //////////////////////////////// @@ -39,23 +39,26 @@ struct MidiEngine { static void midi_read_callback(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) { (void)readProcRefCon; - MidiEngine *engine = (MidiEngine *)readProcRefCon; - S32 device_idx = (S32)(intptr_t)srcConnRefCon; + MidiEngine *engine = (MidiEngine *)readProcRefCon; + S32 device_idx = (S32)(intptr_t)srcConnRefCon; if (!engine || device_idx < 0 || device_idx >= MIDI_MAX_DEVICES) return; const MIDIPacket *packet = &pktlist->packet[0]; for (UInt32 i = 0; i < pktlist->numPackets; i++) { // Parse MIDI bytes - for (UInt16 j = 0; j < packet->length; ) { + for (UInt16 j = 0; j < packet->length;) { U8 status = packet->data[j]; // Skip non-status bytes (running status not handled for simplicity) - if (status < 0x80) { j++; continue; } + if (status < 0x80) { + j++; + continue; + } U8 kind = status & 0xF0; if (kind == 0x90 && j + 2 < packet->length) { - U8 note = packet->data[j + 1]; + U8 note = packet->data[j + 1]; U8 velocity = packet->data[j + 2]; j += 3; @@ -92,7 +95,8 @@ static void midi_read_callback(const MIDIPacketList *pktlist, void *readProcRefC } else if (kind == 0xF0) { // System messages — skip to end or next status byte j++; - while (j < packet->length && packet->data[j] < 0x80) j++; + while (j < packet->length && packet->data[j] < 0x80) + j++; } else { j += 3; // Default: 3-byte message } @@ -126,9 +130,9 @@ static void enumerate_midi_devices(MidiEngine *engine) { snprintf(dev->name, sizeof(dev->name), "MIDI Source %d", (S32)i); } - dev->id = engine->device_count; + dev->id = engine->device_count; dev->is_input = true; - dev->active = false; + dev->active = false; engine->source_to_device[i] = engine->device_count; engine->device_count++; @@ -152,9 +156,9 @@ static void enumerate_midi_devices(MidiEngine *engine) { snprintf(dev->name, sizeof(dev->name), "MIDI Dest %d", (S32)i); } - dev->id = engine->device_count; + dev->id = engine->device_count; dev->is_input = false; - dev->active = false; + dev->active = false; engine->device_count++; } @@ -185,7 +189,7 @@ void midi_open_all_inputs(MidiEngine *engine) { ItemCount num_sources = MIDIGetNumberOfSources(); for (ItemCount i = 0; i < num_sources && (S32)i < MIDI_MAX_DEVICES; i++) { MIDIEndpointRef endpoint = MIDIGetSource(i); - S32 dev_idx = engine->source_to_device[i]; + S32 dev_idx = engine->source_to_device[i]; MIDIPortConnectSource(engine->input_port, endpoint, (void *)(intptr_t)dev_idx); } } @@ -203,8 +207,8 @@ void midi_close_all_inputs(MidiEngine *engine) { atomic_store(&engine->pending_note_off[i], 0); atomic_store(&engine->held_note_count[i], 0); engine->display_velocity[i] = 0; - engine->display_note[i] = 0; - engine->release_timers[i] = 0.0f; + engine->display_note[i] = 0; + engine->release_timers[i] = 0.0f; } for (S32 i = 0; i < 128; i++) { atomic_store(&engine->note_states[i], 0); @@ -216,36 +220,36 @@ void midi_update(MidiEngine *engine, F32 dt) { for (S32 i = 0; i < engine->device_count; i++) { if (!engine->devices[i].is_input) continue; - S32 vel = atomic_exchange(&engine->pending_note_on_vel[i], 0); + S32 vel = atomic_exchange(&engine->pending_note_on_vel[i], 0); S32 note_p1 = atomic_exchange(&engine->pending_note_num[i], 0); if (vel > 0) engine->display_velocity[i] = vel; if (note_p1 > 0) engine->display_note[i] = note_p1 - 1; - S32 off = atomic_exchange(&engine->pending_note_off[i], 0); + S32 off = atomic_exchange(&engine->pending_note_off[i], 0); S32 held = atomic_load(&engine->held_note_count[i]); if (held > 0) { - engine->devices[i].active = true; + engine->devices[i].active = true; engine->devices[i].releasing = false; - engine->release_timers[i] = 0.0f; + engine->release_timers[i] = 0.0f; } else if (off || (engine->devices[i].active && held <= 0)) { - engine->devices[i].active = false; + engine->devices[i].active = false; engine->devices[i].releasing = true; - engine->release_timers[i] = MIDI_RELEASE_FLASH_DURATION; + engine->release_timers[i] = MIDI_RELEASE_FLASH_DURATION; } if (engine->release_timers[i] > 0.0f) { engine->release_timers[i] -= dt; if (engine->release_timers[i] <= 0.0f) { - engine->release_timers[i] = 0.0f; + engine->release_timers[i] = 0.0f; engine->devices[i].releasing = false; - engine->display_velocity[i] = 0; - engine->display_note[i] = 0; + engine->display_velocity[i] = 0; + engine->display_note[i] = 0; } } engine->devices[i].velocity = engine->display_velocity[i]; - engine->devices[i].note = engine->display_note[i]; + engine->devices[i].note = engine->display_note[i]; } } diff --git a/src/midi/midi_win32.cpp b/src/midi/midi_win32.cpp index 4e20f85..15abe49 100644 --- a/src/midi/midi_win32.cpp +++ b/src/midi/midi_win32.cpp @@ -1,31 +1,31 @@ #include "midi/midi.h" -#include #include #include +#include -#define MIDI_MAX_DEVICES 64 +#define MIDI_MAX_DEVICES 64 #define MIDI_RELEASE_FLASH_DURATION 0.15f struct MidiEngine { MidiDeviceInfo devices[MIDI_MAX_DEVICES]; - S32 device_count; + S32 device_count; - HMIDIIN input_handles[MIDI_MAX_DEVICES]; + HMIDIIN input_handles[MIDI_MAX_DEVICES]; // Set atomically from callback thread - volatile LONG pending_note_on_vel[MIDI_MAX_DEVICES]; // last note-on velocity (0 = consumed) - volatile LONG pending_note_num[MIDI_MAX_DEVICES]; // last note-on note number + 1 (0 = consumed) - volatile LONG pending_note_off[MIDI_MAX_DEVICES]; // note-off received flag - volatile LONG held_note_count[MIDI_MAX_DEVICES]; // number of notes currently held + volatile LONG pending_note_on_vel[MIDI_MAX_DEVICES]; // last note-on velocity (0 = consumed) + volatile LONG pending_note_num[MIDI_MAX_DEVICES]; // last note-on note number + 1 (0 = consumed) + volatile LONG pending_note_off[MIDI_MAX_DEVICES]; // note-off received flag + volatile LONG held_note_count[MIDI_MAX_DEVICES]; // number of notes currently held // Per-note state (across all devices), set atomically from callback - volatile LONG note_states[128]; // held count - volatile LONG note_velocities[128]; // last note-on velocity + volatile LONG note_states[128]; // held count + volatile LONG note_velocities[128]; // last note-on velocity // Main thread only - S32 display_velocity[MIDI_MAX_DEVICES]; - S32 display_note[MIDI_MAX_DEVICES]; - F32 release_timers[MIDI_MAX_DEVICES]; + S32 display_velocity[MIDI_MAX_DEVICES]; + S32 display_note[MIDI_MAX_DEVICES]; + F32 release_timers[MIDI_MAX_DEVICES]; }; //////////////////////////////// @@ -34,7 +34,7 @@ struct MidiEngine { static MidiEngine *g_midi_engine = nullptr; static void CALLBACK midi_in_callback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, - DWORD_PTR dwParam1, DWORD_PTR dwParam2) { + DWORD_PTR dwParam1, DWORD_PTR dwParam2) { (void)hMidiIn; (void)dwParam2; @@ -91,10 +91,10 @@ void midi_open_all_inputs(MidiEngine *engine) { if (!dev->is_input) continue; if (engine->input_handles[i]) continue; // already open - HMIDIIN handle = nullptr; - MMRESULT res = midiInOpen(&handle, (UINT)dev->id, - (DWORD_PTR)midi_in_callback, - (DWORD_PTR)i, CALLBACK_FUNCTION); + HMIDIIN handle = nullptr; + MMRESULT res = midiInOpen(&handle, (UINT)dev->id, + (DWORD_PTR)midi_in_callback, + (DWORD_PTR)i, CALLBACK_FUNCTION); if (res == MMSYSERR_NOERROR) { engine->input_handles[i] = handle; midiInStart(handle); @@ -110,15 +110,15 @@ void midi_close_all_inputs(MidiEngine *engine) { engine->input_handles[i] = nullptr; } engine->pending_note_on_vel[i] = 0; - engine->pending_note_num[i] = 0; - engine->pending_note_off[i] = 0; - engine->held_note_count[i] = 0; - engine->display_velocity[i] = 0; - engine->display_note[i] = 0; - engine->release_timers[i] = 0.0f; + engine->pending_note_num[i] = 0; + engine->pending_note_off[i] = 0; + engine->held_note_count[i] = 0; + engine->display_velocity[i] = 0; + engine->display_note[i] = 0; + engine->release_timers[i] = 0.0f; } for (S32 i = 0; i < 128; i++) { - engine->note_states[i] = 0; + engine->note_states[i] = 0; engine->note_velocities[i] = 0; } } @@ -128,7 +128,7 @@ void midi_update(MidiEngine *engine, F32 dt) { if (!engine->devices[i].is_input) continue; // Consume pending note-on velocity and note number - LONG vel = InterlockedExchange(&engine->pending_note_on_vel[i], 0); + LONG vel = InterlockedExchange(&engine->pending_note_on_vel[i], 0); LONG note_p1 = InterlockedExchange(&engine->pending_note_num[i], 0); if (vel > 0) { engine->display_velocity[i] = (S32)vel; @@ -144,29 +144,29 @@ void midi_update(MidiEngine *engine, F32 dt) { LONG held = engine->held_note_count[i]; if (held > 0) { - engine->devices[i].active = true; + engine->devices[i].active = true; engine->devices[i].releasing = false; - engine->release_timers[i] = 0.0f; + engine->release_timers[i] = 0.0f; } else if (off || (engine->devices[i].active && held <= 0)) { // All notes just released — start release flash - engine->devices[i].active = false; + engine->devices[i].active = false; engine->devices[i].releasing = true; - engine->release_timers[i] = MIDI_RELEASE_FLASH_DURATION; + engine->release_timers[i] = MIDI_RELEASE_FLASH_DURATION; } // Decay release flash timer if (engine->release_timers[i] > 0.0f) { engine->release_timers[i] -= dt; if (engine->release_timers[i] <= 0.0f) { - engine->release_timers[i] = 0.0f; + engine->release_timers[i] = 0.0f; engine->devices[i].releasing = false; - engine->display_velocity[i] = 0; - engine->display_note[i] = 0; + engine->display_velocity[i] = 0; + engine->display_note[i] = 0; } } engine->devices[i].velocity = engine->display_velocity[i]; - engine->devices[i].note = engine->display_note[i]; + engine->devices[i].note = engine->display_note[i]; } } @@ -197,9 +197,9 @@ void midi_refresh_devices(MidiEngine *engine) { if (midiInGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { MidiDeviceInfo *dev = &engine->devices[engine->device_count++]; strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE); - dev->id = (S32)i; + dev->id = (S32)i; dev->is_input = true; - dev->active = false; + dev->active = false; } } @@ -209,9 +209,9 @@ void midi_refresh_devices(MidiEngine *engine) { if (midiOutGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { MidiDeviceInfo *dev = &engine->devices[engine->device_count++]; strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE); - dev->id = (S32)i; + dev->id = (S32)i; dev->is_input = false; - dev->active = false; + dev->active = false; } } diff --git a/src/platform/platform.h b/src/platform/platform.h index 0cd8859..1be3deb 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -27,45 +27,45 @@ enum { PKEY_V = 0x56, PKEY_X = 0x58, PKEY_0 = 0x30, - PKEY_EQUAL = 0xBB, // '='/'+' (VK_OEM_PLUS) - PKEY_MINUS = 0xBD, // '-'/'_' (VK_OEM_MINUS) + PKEY_EQUAL = 0xBB, // '='/'+' (VK_OEM_PLUS) + PKEY_MINUS = 0xBD, // '-'/'_' (VK_OEM_MINUS) }; struct PlatformInput { // Typed characters (UTF-16 code units, printable only) U16 chars[PLATFORM_MAX_CHARS_PER_FRAME]; - S32 char_count; + S32 char_count; // Key-down events (virtual key codes) U8 keys[PLATFORM_MAX_KEYS_PER_FRAME]; - S32 key_count; + S32 key_count; // Modifier state at time of last key event - B32 ctrl_held; - B32 shift_held; + B32 ctrl_held; + B32 shift_held; // Mouse state (polled per frame) - Vec2F32 mouse_pos; - Vec2F32 scroll_delta; - B32 mouse_down; - B32 was_mouse_down; + Vec2F32 mouse_pos; + Vec2F32 scroll_delta; + B32 mouse_down; + B32 was_mouse_down; }; struct PlatformWindow; enum PlatformWindowStyle { - PLATFORM_WINDOW_STYLE_NORMAL = 0, - PLATFORM_WINDOW_STYLE_POPUP = 1, // utility panel, owned by parent, fixed size - PLATFORM_WINDOW_STYLE_POPUP_RESIZABLE = 2, // utility panel, owned by parent, resizable + PLATFORM_WINDOW_STYLE_NORMAL = 0, + PLATFORM_WINDOW_STYLE_POPUP = 1, // utility panel, owned by parent, fixed size + PLATFORM_WINDOW_STYLE_POPUP_RESIZABLE = 2, // utility panel, owned by parent, resizable }; struct PlatformWindowDesc { - const char *title = "autosample"; - S32 width = 1280; - S32 height = 720; - PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_NORMAL; - PlatformWindow *parent = nullptr; - B32 independent = 0; // if true, don't attach as child (independent top-level window) + const char *title = "autosample"; + S32 width = 1280; + S32 height = 720; + PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_NORMAL; + PlatformWindow *parent = nullptr; + B32 independent = 0; // if true, don't attach as child (independent top-level window) }; enum PlatformMsgBoxType { @@ -75,14 +75,14 @@ enum PlatformMsgBoxType { }; struct PlatformMenuItem { - const char *label; // nullptr = separator - S32 id; // command ID (ignored for separators) + const char *label; // nullptr = separator + S32 id; // command ID (ignored for separators) }; struct PlatformMenu { - const char *label; - PlatformMenuItem *items; - S32 item_count; + const char *label; + PlatformMenuItem *items; + S32 item_count; }; // Called by the platform layer when the window needs a frame rendered @@ -92,13 +92,13 @@ typedef void (*PlatformFrameCallback)(void *user_data); PlatformWindow *platform_create_window(PlatformWindowDesc *desc); void platform_destroy_window(PlatformWindow *window); -B32 platform_poll_events(PlatformWindow *window); +B32 platform_poll_events(PlatformWindow *window); void platform_get_size(PlatformWindow *window, S32 *w, S32 *h); void *platform_get_native_handle(PlatformWindow *window); -void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data); -void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_count); -S32 platform_poll_menu_command(PlatformWindow *window); +void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data); +void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_count); +S32 platform_poll_menu_command(PlatformWindow *window); // Returns accumulated input since last call (keyboard events + polled mouse state), then clears the buffer. PlatformInput platform_get_input(PlatformWindow *window); @@ -116,8 +116,8 @@ S32 platform_message_box(PlatformWindow *parent, const char *title, // Cursor shapes for resize handles enum PlatformCursor { PLATFORM_CURSOR_ARROW = 0, - PLATFORM_CURSOR_SIZE_WE = 1, // horizontal resize - PLATFORM_CURSOR_SIZE_NS = 2, // vertical resize + PLATFORM_CURSOR_SIZE_WE = 1, // horizontal resize + PLATFORM_CURSOR_SIZE_NS = 2, // vertical resize }; void platform_set_cursor(PlatformCursor cursor); diff --git a/src/platform/platform_macos.mm b/src/platform/platform_macos.mm index fb857fe..67a3ec9 100644 --- a/src/platform/platform_macos.mm +++ b/src/platform/platform_macos.mm @@ -4,42 +4,53 @@ // macOS virtual key codes (avoids Carbon.h include) enum { - kVK_ANSI_A = 0x00, kVK_ANSI_C = 0x08, kVK_ANSI_V = 0x09, - kVK_ANSI_X = 0x07, - kVK_Return = 0x24, kVK_Tab = 0x30, kVK_Delete = 0x33, - kVK_Escape = 0x35, kVK_ForwardDelete = 0x75, - kVK_LeftArrow = 0x7B, kVK_RightArrow = 0x7C, - kVK_DownArrow = 0x7D, kVK_UpArrow = 0x7E, - kVK_Home = 0x73, kVK_End = 0x77, - kVK_Command = 0x37, kVK_Shift = 0x38, - kVK_RightShift = 0x3C, kVK_RightCommand = 0x36, - kVK_ANSI_Equal = 0x18, kVK_ANSI_Minus = 0x1B, - kVK_ANSI_0 = 0x1D, + kVK_ANSI_A = 0x00, + kVK_ANSI_C = 0x08, + kVK_ANSI_V = 0x09, + kVK_ANSI_X = 0x07, + kVK_Return = 0x24, + kVK_Tab = 0x30, + kVK_Delete = 0x33, + kVK_Escape = 0x35, + kVK_ForwardDelete = 0x75, + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E, + kVK_Home = 0x73, + kVK_End = 0x77, + kVK_Command = 0x37, + kVK_Shift = 0x38, + kVK_RightShift = 0x3C, + kVK_RightCommand = 0x36, + kVK_ANSI_Equal = 0x18, + kVK_ANSI_Minus = 0x1B, + kVK_ANSI_0 = 0x1D, kVK_ANSI_KeypadEnter = 0x4C, }; static U8 macos_keycode_to_pkey(U16 keycode) { switch (keycode) { - case kVK_ANSI_A: return PKEY_A; - case kVK_ANSI_C: return PKEY_C; - case kVK_ANSI_V: return PKEY_V; - case kVK_ANSI_X: return PKEY_X; - case kVK_Return: return PKEY_RETURN; + case kVK_ANSI_A: return PKEY_A; + case kVK_ANSI_C: return PKEY_C; + case kVK_ANSI_V: return PKEY_V; + case kVK_ANSI_X: return PKEY_X; + case kVK_Return: return PKEY_RETURN; case kVK_ANSI_KeypadEnter: return PKEY_RETURN; - case kVK_Tab: return PKEY_TAB; - case kVK_Delete: return PKEY_BACKSPACE; - case kVK_ForwardDelete:return PKEY_DELETE; - case kVK_Escape: return PKEY_ESCAPE; - case kVK_LeftArrow: return PKEY_LEFT; - case kVK_RightArrow: return PKEY_RIGHT; - case kVK_UpArrow: return PKEY_UP; - case kVK_DownArrow: return PKEY_DOWN; - case kVK_Home: return PKEY_HOME; - case kVK_End: return PKEY_END; - case kVK_ANSI_Equal: return PKEY_EQUAL; - case kVK_ANSI_Minus: return PKEY_MINUS; - case kVK_ANSI_0: return PKEY_0; - default: return 0; + case kVK_Tab: return PKEY_TAB; + case kVK_Delete: return PKEY_BACKSPACE; + case kVK_ForwardDelete: return PKEY_DELETE; + case kVK_Escape: return PKEY_ESCAPE; + case kVK_LeftArrow: return PKEY_LEFT; + case kVK_RightArrow: return PKEY_RIGHT; + case kVK_UpArrow: return PKEY_UP; + case kVK_DownArrow: return PKEY_DOWN; + case kVK_Home: return PKEY_HOME; + case kVK_End: return PKEY_END; + case kVK_ANSI_Equal: return PKEY_EQUAL; + case kVK_ANSI_Minus: return PKEY_MINUS; + case kVK_ANSI_0: return PKEY_0; + default: return 0; } } @@ -58,18 +69,23 @@ static PlatformWindow *g_main_window = nullptr; @end @implementation ASmplAppDelegate -- (void)applicationDidFinishLaunching:(NSNotification *)notification { (void)notification; } -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { (void)sender; return YES; } +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + (void)notification; +} +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + (void)sender; + return YES; +} @end @interface ASmplWindowDelegate : NSObject { -@public + @public PlatformWindow *_platformWindow; } @end @interface ASmplView : NSView { -@public + @public PlatformWindow *_platformWindow; } @end @@ -81,10 +97,10 @@ struct PlatformWindow { NSWindow *ns_window; ASmplView *view; ASmplWindowDelegate *delegate; - B32 should_close; - S32 width; - S32 height; - S32 pending_menu_cmd; + B32 should_close; + S32 width; + S32 height; + S32 pending_menu_cmd; PlatformFrameCallback frame_callback; void *frame_callback_user_data; PlatformInput input; @@ -101,16 +117,19 @@ static void platform_macos_insert_text_pw(PlatformWindow *pw, const char *utf8) PlatformInput *ev = &pw->input; while (*utf8 && ev->char_count < PLATFORM_MAX_CHARS_PER_FRAME) { U8 c = (U8)*utf8; - if (c < 32) { utf8++; continue; } + if (c < 32) { + utf8++; + continue; + } // Handle ASCII printable range (single-byte UTF-8) if (c < 0x80) { ev->chars[ev->char_count++] = (U16)c; utf8++; } else { // Skip multi-byte UTF-8 sequences for now (UI only handles ASCII) - if (c < 0xE0) utf8 += 2; + if (c < 0xE0) utf8 += 2; else if (c < 0xF0) utf8 += 3; - else utf8 += 4; + else utf8 += 4; } } } @@ -143,11 +162,11 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM - (void)windowDidResize:(NSNotification *)notification { (void)notification; if (!_platformWindow) return; - PlatformWindow *pw = _platformWindow; - NSRect frame = [pw->view bounds]; - F32 scale = pw->backing_scale; - pw->width = (S32)(frame.size.width * scale); - pw->height = (S32)(frame.size.height * scale); + PlatformWindow *pw = _platformWindow; + NSRect frame = [pw->view bounds]; + F32 scale = pw->backing_scale; + pw->width = (S32)(frame.size.width * scale); + pw->height = (S32)(frame.size.height * scale); if (pw->frame_callback) pw->frame_callback(pw->frame_callback_user_data); } @@ -155,25 +174,45 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM @implementation ASmplView -- (BOOL)acceptsFirstResponder { return YES; } -- (BOOL)canBecomeKeyView { return YES; } +- (BOOL)acceptsFirstResponder { + return YES; +} +- (BOOL)canBecomeKeyView { + return YES; +} // Needed for NSTextInputClient -- (BOOL)hasMarkedText { return NO; } -- (NSRange)markedRange { return NSMakeRange(NSNotFound, 0); } -- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); } -- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { - (void)string; (void)selectedRange; (void)replacementRange; +- (BOOL)hasMarkedText { + return NO; +} +- (NSRange)markedRange { + return NSMakeRange(NSNotFound, 0); +} +- (NSRange)selectedRange { + return NSMakeRange(NSNotFound, 0); +} +- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { + (void)string; + (void)selectedRange; + (void)replacementRange; +} +- (void)unmarkText { +} +- (NSArray *)validAttributesForMarkedText { + return @[]; } -- (void)unmarkText {} -- (NSArray *)validAttributesForMarkedText { return @[]; } - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - (void)range; (void)actualRange; + (void)range; + (void)actualRange; return nil; } -- (NSUInteger)characterIndexForPoint:(NSPoint)point { (void)point; return NSNotFound; } +- (NSUInteger)characterIndexForPoint:(NSPoint)point { + (void)point; + return NSNotFound; +} - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - (void)range; (void)actualRange; + (void)range; + (void)actualRange; return NSZeroRect; } @@ -192,7 +231,7 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM platform_macos_key_down_pw(_platformWindow, [event keyCode], [event modifierFlags]); // Feed into text input system for character generation - [self interpretKeyEvents:@[event]]; + [self interpretKeyEvents:@[ event ]]; } - (void)flagsChanged:(NSEvent *)event { @@ -210,8 +249,12 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM if (_platformWindow) _platformWindow->mouse_down_state = 0; } -- (void)mouseMoved:(NSEvent *)event { (void)event; } -- (void)mouseDragged:(NSEvent *)event { (void)event; } +- (void)mouseMoved:(NSEvent *)event { + (void)event; +} +- (void)mouseDragged:(NSEvent *)event { + (void)event; +} - (void)scrollWheel:(NSEvent *)event { if (!_platformWindow) return; @@ -221,7 +264,10 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM _platformWindow->input.scroll_delta.y += dy; } -- (BOOL)acceptsFirstMouse:(NSEvent *)event { (void)event; return YES; } +- (BOOL)acceptsFirstMouse:(NSEvent *)event { + (void)event; + return YES; +} @end @@ -235,7 +281,7 @@ static void platform_macos_key_down_pw(PlatformWindow *pw, U16 keycode, NSEventM @implementation ASmplMenuTarget - (void)menuAction:(id)sender { if (!g_main_window) return; - NSMenuItem *item = (NSMenuItem *)sender; + NSMenuItem *item = (NSMenuItem *)sender; g_main_window->pending_menu_cmd = (S32)[item tag]; } @end @@ -271,9 +317,9 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { NSWindow *ns_window = [[NSWindow alloc] initWithContentRect:content_rect - styleMask:style_mask - backing:NSBackingStoreBuffered - defer:NO]; + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]; [ns_window setTitle:[NSString stringWithUTF8String:desc->title]]; [ns_window center]; @@ -290,16 +336,16 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { PlatformWindow *window = new PlatformWindow(); memset(window, 0, sizeof(*window)); - window->ns_window = ns_window; - window->view = view; - window->delegate = delegate; - window->should_close = false; + window->ns_window = ns_window; + window->view = view; + window->delegate = delegate; + window->should_close = false; window->backing_scale = (F32)[ns_window backingScaleFactor]; - window->width = (S32)(desc->width * window->backing_scale); - window->height = (S32)(desc->height * window->backing_scale); + window->width = (S32)(desc->width * window->backing_scale); + window->height = (S32)(desc->height * window->backing_scale); // Wire up per-window pointers - view->_platformWindow = window; + view->_platformWindow = window; delegate->_platformWindow = window; // Track main window for menu commands @@ -338,8 +384,8 @@ B32 platform_poll_events(PlatformWindow *window) { // When the app is not active (e.g. during Cmd+Tab away), block until // an event arrives instead of spinning. This keeps CPU near zero while // backgrounded and lets macOS handle the app-switch animation smoothly. - BOOL is_active = [NSApp isActive]; - NSDate *deadline = is_active ? nil : [NSDate distantFuture]; + BOOL is_active = [NSApp isActive]; + NSDate *deadline = is_active ? nil : [NSDate distantFuture]; NSEvent *event; while ((event = [NSApp nextEventMatchingMask:NSEventMaskAny @@ -366,7 +412,7 @@ void *platform_get_native_handle(PlatformWindow *window) { } void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data) { - window->frame_callback = cb; + window->frame_callback = cb; window->frame_callback_user_data = user_data; } @@ -380,7 +426,7 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou // App menu (required on macOS) NSMenuItem *app_menu_item = [[NSMenuItem alloc] init]; - NSMenu *app_menu = [[NSMenu alloc] init]; + NSMenu *app_menu = [[NSMenu alloc] init]; [app_menu addItemWithTitle:@"Quit autosample" action:@selector(terminate:) keyEquivalent:@"q"]; @@ -389,8 +435,8 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou for (S32 i = 0; i < menu_count; i++) { NSMenuItem *top_item = [[NSMenuItem alloc] init]; - NSMenu *submenu = [[NSMenu alloc] initWithTitle: - [NSString stringWithUTF8String:menus[i].label]]; + NSMenu *submenu = [[NSMenu alloc] initWithTitle: + [NSString stringWithUTF8String:menus[i].label]]; for (S32 j = 0; j < menus[i].item_count; j++) { PlatformMenuItem *item = &menus[i].items[j]; @@ -399,7 +445,7 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou } else { NSMenuItem *ns_item = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:item->label] - action:@selector(menuAction:) + action:@selector(menuAction:) keyEquivalent:@""]; [ns_item setTag:item->id]; [ns_item setTarget:g_menu_target]; @@ -415,7 +461,7 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou } S32 platform_poll_menu_command(PlatformWindow *window) { - S32 cmd = window->pending_menu_cmd; + S32 cmd = window->pending_menu_cmd; window->pending_menu_cmd = 0; return cmd; } @@ -425,21 +471,21 @@ PlatformInput platform_get_input(PlatformWindow *window) { // Poll mouse position (Cocoa uses bottom-left origin, flip Y) NSPoint mouse_in_window = [window->ns_window mouseLocationOutsideOfEventStream]; - NSRect view_bounds = [window->view bounds]; - F32 scale = window->backing_scale; - result.mouse_pos = v2f32( + NSRect view_bounds = [window->view bounds]; + F32 scale = window->backing_scale; + result.mouse_pos = v2f32( (F32)mouse_in_window.x * scale, (F32)(view_bounds.size.height - mouse_in_window.y) * scale); // Mouse button state - result.was_mouse_down = window->prev_mouse_down; - result.mouse_down = window->mouse_down_state; + result.was_mouse_down = window->prev_mouse_down; + result.mouse_down = window->mouse_down_state; window->prev_mouse_down = result.mouse_down; // Poll current modifier state (so shift/ctrl are accurate even without key events) NSEventModifierFlags mods = [NSEvent modifierFlags]; - result.ctrl_held = (mods & NSEventModifierFlagCommand) != 0; - result.shift_held = (mods & NSEventModifierFlagShift) != 0; + result.ctrl_held = (mods & NSEventModifierFlagCommand) != 0; + result.shift_held = (mods & NSEventModifierFlagShift) != 0; // Clear accumulated events for next frame window->input = {}; @@ -463,8 +509,8 @@ F32 platform_get_dpi_scale(PlatformWindow *window) { void platform_set_cursor(PlatformCursor cursor) { switch (cursor) { case PLATFORM_CURSOR_SIZE_WE: [[NSCursor resizeLeftRightCursor] set]; break; - case PLATFORM_CURSOR_SIZE_NS: [[NSCursor resizeUpDownCursor] set]; break; - default: [[NSCursor arrowCursor] set]; break; + case PLATFORM_CURSOR_SIZE_NS: [[NSCursor resizeUpDownCursor] set]; break; + default: [[NSCursor arrowCursor] set]; break; } } @@ -479,8 +525,8 @@ const char *platform_clipboard_get() { static char buf[4096]; buf[0] = '\0'; - NSPasteboard *pb = [NSPasteboard generalPasteboard]; - NSString *str = [pb stringForType:NSPasteboardTypeString]; + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + NSString *str = [pb stringForType:NSPasteboardTypeString]; if (str) { const char *utf8 = [str UTF8String]; if (utf8) { diff --git a/src/platform/platform_win32.cpp b/src/platform/platform_win32.cpp index 1b68d22..f7f3a9b 100644 --- a/src/platform/platform_win32.cpp +++ b/src/platform/platform_win32.cpp @@ -3,15 +3,15 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif -#include #include +#include struct PlatformWindow { - HWND hwnd; - B32 should_close; - S32 width; - S32 height; - S32 pending_menu_cmd; + HWND hwnd; + B32 should_close; + S32 width; + S32 height; + S32 pending_menu_cmd; PlatformFrameCallback frame_callback; void *frame_callback_user_data; PlatformInput input; @@ -19,9 +19,9 @@ struct PlatformWindow { }; // Main window receives menu commands -static PlatformWindow *g_main_window = nullptr; -static HCURSOR g_current_cursor = nullptr; -static B32 g_wndclass_registered = false; +static PlatformWindow *g_main_window = nullptr; +static HCURSOR g_current_cursor = nullptr; +static B32 g_wndclass_registered = false; static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { PlatformWindow *pw = (PlatformWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); @@ -77,8 +77,8 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM if (pw) { RECT *suggested = (RECT *)lparam; SetWindowPos(hwnd, nullptr, suggested->left, suggested->top, - suggested->right - suggested->left, suggested->bottom - suggested->top, - SWP_NOZORDER | SWP_NOACTIVATE); + suggested->right - suggested->left, suggested->bottom - suggested->top, + SWP_NOZORDER | SWP_NOACTIVATE); } return 0; case WM_CLOSE: @@ -101,7 +101,7 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (!g_wndclass_registered) { - WNDCLASSEXW wc = {}; + WNDCLASSEXW wc = {}; wc.cbSize = sizeof(wc); wc.style = CS_CLASSDC; wc.lpfnWndProc = win32_wndproc; @@ -112,14 +112,14 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { g_wndclass_registered = true; } - UINT dpi = GetDpiForSystem(); - int screen_w = GetSystemMetrics(SM_CXSCREEN); - int screen_h = GetSystemMetrics(SM_CYSCREEN); - int x = (screen_w - desc->width) / 2; - int y = (screen_h - desc->height) / 2; + UINT dpi = GetDpiForSystem(); + int screen_w = GetSystemMetrics(SM_CXSCREEN); + int screen_h = GetSystemMetrics(SM_CYSCREEN); + int x = (screen_w - desc->width) / 2; + int y = (screen_h - desc->height) / 2; DWORD style; - HWND parent_hwnd = nullptr; + HWND parent_hwnd = nullptr; if (desc->style == PLATFORM_WINDOW_STYLE_POPUP) { style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; if (desc->parent && !desc->independent) parent_hwnd = desc->parent->hwnd; @@ -133,8 +133,8 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { RECT rect = { 0, 0, (LONG)desc->width, (LONG)desc->height }; AdjustWindowRectExForDpi(&rect, style, FALSE, 0, dpi); - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, nullptr, 0); - wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, nullptr, 0); + wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, wtitle, wchar_count); HWND hwnd = CreateWindowExW( @@ -143,8 +143,7 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { x, y, rect.right - rect.left, rect.bottom - rect.top, - parent_hwnd, nullptr, GetModuleHandleW(nullptr), nullptr - ); + parent_hwnd, nullptr, GetModuleHandleW(nullptr), nullptr); _freea(wtitle); @@ -152,10 +151,10 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { return nullptr; PlatformWindow *window = new PlatformWindow(); - window->hwnd = hwnd; - window->should_close = false; - window->width = desc->width; - window->height = desc->height; + window->hwnd = hwnd; + window->should_close = false; + window->width = desc->width; + window->height = desc->height; // Store PlatformWindow* on the HWND so WndProc can find it SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window); @@ -206,7 +205,7 @@ void *platform_get_native_handle(PlatformWindow *window) { } void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data) { - window->frame_callback = cb; + window->frame_callback = cb; window->frame_callback_user_data = user_data; } @@ -221,16 +220,16 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou if (!item->label) { AppendMenuW(submenu, MF_SEPARATOR, 0, nullptr); } else { - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, nullptr, 0); - wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, nullptr, 0); + wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, item->label, -1, wlabel, wchar_count); AppendMenuW(submenu, MF_STRING, (UINT_PTR)item->id, wlabel); _freea(wlabel); } } - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, nullptr, 0); - wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, nullptr, 0); + wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, wlabel, wchar_count); AppendMenuW(menu_bar, MF_POPUP, (UINT_PTR)submenu, wlabel); _freea(wlabel); @@ -240,7 +239,7 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou } S32 platform_poll_menu_command(PlatformWindow *window) { - S32 cmd = window->pending_menu_cmd; + S32 cmd = window->pending_menu_cmd; window->pending_menu_cmd = 0; return cmd; } @@ -255,8 +254,8 @@ PlatformInput platform_get_input(PlatformWindow *window) { result.mouse_pos = v2f32((F32)cursor.x, (F32)cursor.y); // Poll mouse button - result.was_mouse_down = window->prev_mouse_down; - result.mouse_down = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0; + result.was_mouse_down = window->prev_mouse_down; + result.mouse_down = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0; window->prev_mouse_down = result.mouse_down; // Clear accumulated events for next frame @@ -283,7 +282,7 @@ void platform_set_cursor(PlatformCursor cursor) { switch (cursor) { case PLATFORM_CURSOR_SIZE_WE: g_current_cursor = LoadCursor(nullptr, IDC_SIZEWE); break; case PLATFORM_CURSOR_SIZE_NS: g_current_cursor = LoadCursor(nullptr, IDC_SIZENS); break; - default: g_current_cursor = LoadCursor(nullptr, IDC_ARROW); break; + default: g_current_cursor = LoadCursor(nullptr, IDC_ARROW); break; } } @@ -325,7 +324,7 @@ const char *platform_clipboard_get() { if (hmem) { wchar_t *wbuf = (wchar_t *)GlobalLock(hmem); if (wbuf) { - int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf) - 1, nullptr, nullptr); + int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf) - 1, nullptr, nullptr); buf[len > 0 ? len - 1 : 0] = '\0'; // WideCharToMultiByte includes null in count GlobalUnlock(hmem); } @@ -339,32 +338,32 @@ S32 platform_message_box(PlatformWindow *parent, const char *title, const char *message, PlatformMsgBoxType type) { UINT mb_type; switch (type) { - case PLATFORM_MSGBOX_OK: mb_type = MB_OK; break; + case PLATFORM_MSGBOX_OK: mb_type = MB_OK; break; case PLATFORM_MSGBOX_OK_CANCEL: mb_type = MB_OKCANCEL; break; - case PLATFORM_MSGBOX_YES_NO: mb_type = MB_YESNO; break; - default: mb_type = MB_OK; break; + case PLATFORM_MSGBOX_YES_NO: mb_type = MB_YESNO; break; + default: mb_type = MB_OK; break; } // Convert UTF-8 to wide strings - int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0); - wchar_t *wtitle = (wchar_t *)_malloca(title_wlen * sizeof(wchar_t)); + int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0); + wchar_t *wtitle = (wchar_t *)_malloca(title_wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, title, -1, wtitle, title_wlen); - int msg_wlen = MultiByteToWideChar(CP_UTF8, 0, message, -1, nullptr, 0); - wchar_t *wmsg = (wchar_t *)_malloca(msg_wlen * sizeof(wchar_t)); + int msg_wlen = MultiByteToWideChar(CP_UTF8, 0, message, -1, nullptr, 0); + wchar_t *wmsg = (wchar_t *)_malloca(msg_wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, message, -1, wmsg, msg_wlen); - HWND hwnd = parent ? parent->hwnd : nullptr; - int result = MessageBoxW(hwnd, wmsg, wtitle, mb_type); + HWND hwnd = parent ? parent->hwnd : nullptr; + int result = MessageBoxW(hwnd, wmsg, wtitle, mb_type); _freea(wmsg); _freea(wtitle); switch (result) { - case IDOK: return 0; - case IDYES: return 0; + case IDOK: return 0; + case IDYES: return 0; case IDCANCEL: return 1; - case IDNO: return 1; - default: return -1; + case IDNO: return 1; + default: return -1; } } diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 6b0d210..2f3318b 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -10,10 +10,10 @@ typedef struct Renderer Renderer; struct Clay_RenderCommandArray; typedef struct RendererDesc { - void *window_handle; - S32 width; - S32 height; - S32 frame_count; + void *window_handle; + S32 width; + S32 height; + S32 frame_count; } RendererDesc; Renderer *renderer_create(RendererDesc *desc); @@ -24,13 +24,13 @@ Renderer *renderer_create(RendererDesc *desc); // windows. Renderer *renderer_create_shared(Renderer *parent, RendererDesc *desc); -void renderer_destroy(Renderer *renderer); -B32 renderer_begin_frame(Renderer *renderer); -void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands); -void renderer_resize(Renderer *renderer, S32 width, S32 height); -void renderer_set_font_scale(Renderer *renderer, F32 scale); -void renderer_sync_from_parent(Renderer *renderer); // sync shared font atlas from parent -void renderer_set_clear_color(Renderer *renderer, F32 r, F32 g, F32 b); +void renderer_destroy(Renderer *renderer); +B32 renderer_begin_frame(Renderer *renderer); +void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands); +void renderer_resize(Renderer *renderer, S32 width, S32 height); +void renderer_set_font_scale(Renderer *renderer, F32 scale); +void renderer_sync_from_parent(Renderer *renderer); // sync shared font atlas from parent +void renderer_set_clear_color(Renderer *renderer, F32 r, F32 g, F32 b); // Text measurement callback compatible with UI_MeasureTextFn // Measures text of given length (NOT necessarily null-terminated) at font_size pixels. diff --git a/src/renderer/renderer_vulkan.cpp b/src/renderer/renderer_vulkan.cpp index ad74960..e3501f4 100644 --- a/src/renderer/renderer_vulkan.cpp +++ b/src/renderer/renderer_vulkan.cpp @@ -16,7 +16,7 @@ // VMA — single-file implementation in this translation unit #define VMA_IMPLEMENTATION -#define VMA_STATIC_VULKAN_FUNCTIONS 1 +#define VMA_STATIC_VULKAN_FUNCTIONS 1 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 #include @@ -30,19 +30,19 @@ #include "renderer/font_inter.gen.h" // Embedded SPIR-V shaders (generated at build time by glslc) -#include "renderer/ui_vert.spv.h" #include "renderer/ui_frag.spv.h" +#include "renderer/ui_vert.spv.h" -#define NUM_BACK_BUFFERS 2 -#define MAX_VERTICES (64 * 1024) -#define MAX_INDICES (MAX_VERTICES * 3) +#define NUM_BACK_BUFFERS 2 +#define MAX_VERTICES (64 * 1024) +#define MAX_INDICES (MAX_VERTICES * 3) // Font atlas -#define FONT_ATLAS_W 1024 -#define FONT_ATLAS_H 1024 -#define GLYPH_FIRST 32 -#define GLYPH_LAST 126 -#define GLYPH_COUNT (GLYPH_LAST - GLYPH_FIRST + 1) +#define FONT_ATLAS_W 1024 +#define FONT_ATLAS_H 1024 +#define GLYPH_FIRST 32 +#define GLYPH_LAST 126 +#define GLYPH_COUNT (GLYPH_LAST - GLYPH_FIRST + 1) //////////////////////////////// // Vertex format for 2D UI rendering @@ -73,91 +73,91 @@ typedef struct GlyphInfo { // Frame context typedef struct FrameContext { - VkCommandBuffer command_buffer; - VkFence fence; - U64 fence_value; + VkCommandBuffer command_buffer; + VkFence fence; + U64 fence_value; } FrameContext; //////////////////////////////// // Renderer struct struct Renderer { - Renderer *parent; - HWND hwnd; - S32 width; - S32 height; - S32 frame_count; - U32 frame_index; + Renderer *parent; + HWND hwnd; + S32 width; + S32 height; + S32 frame_count; + U32 frame_index; // Vulkan core - VkInstance instance; - VkPhysicalDevice physical_device; - VkDevice device; - VkQueue graphics_queue; - U32 queue_family; - VmaAllocator allocator; + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + VkQueue graphics_queue; + U32 queue_family; + VmaAllocator allocator; // Surface & swap chain - VkSurfaceKHR surface; - VkSwapchainKHR swap_chain; - VkFormat swap_chain_format; - VkImage swap_chain_images[NUM_BACK_BUFFERS]; - VkImageView swap_chain_views[NUM_BACK_BUFFERS]; - VkFramebuffer framebuffers[NUM_BACK_BUFFERS]; - B32 swap_chain_occluded; + VkSurfaceKHR surface; + VkSwapchainKHR swap_chain; + VkFormat swap_chain_format; + VkImage swap_chain_images[NUM_BACK_BUFFERS]; + VkImageView swap_chain_views[NUM_BACK_BUFFERS]; + VkFramebuffer framebuffers[NUM_BACK_BUFFERS]; + B32 swap_chain_occluded; // Render pass - VkRenderPass render_pass; + VkRenderPass render_pass; // Command pool & per-frame resources - VkCommandPool command_pool; - FrameContext frames[NUM_BACK_BUFFERS]; + VkCommandPool command_pool; + FrameContext frames[NUM_BACK_BUFFERS]; // Synchronization - VkSemaphore image_available_sema[NUM_BACK_BUFFERS]; - VkSemaphore render_finished_sema[NUM_BACK_BUFFERS]; + VkSemaphore image_available_sema[NUM_BACK_BUFFERS]; + VkSemaphore render_finished_sema[NUM_BACK_BUFFERS]; // Pipeline - VkPipelineLayout pipeline_layout; - VkPipeline pipeline; - VkDescriptorSetLayout descriptor_set_layout; + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + VkDescriptorSetLayout descriptor_set_layout; // Descriptor pool & sets - VkDescriptorPool descriptor_pool; - VkDescriptorSet font_descriptor_set; - VkDescriptorSet icon_descriptor_set; - VkSampler sampler; + VkDescriptorPool descriptor_pool; + VkDescriptorSet font_descriptor_set; + VkDescriptorSet icon_descriptor_set; + VkSampler sampler; // Per-frame vertex/index buffers (double-buffered, persistently mapped) - VkBuffer vertex_buffers[NUM_BACK_BUFFERS]; - VmaAllocation vertex_allocs[NUM_BACK_BUFFERS]; - void *vb_mapped[NUM_BACK_BUFFERS]; + VkBuffer vertex_buffers[NUM_BACK_BUFFERS]; + VmaAllocation vertex_allocs[NUM_BACK_BUFFERS]; + void *vb_mapped[NUM_BACK_BUFFERS]; - VkBuffer index_buffers[NUM_BACK_BUFFERS]; - VmaAllocation index_allocs[NUM_BACK_BUFFERS]; - void *ib_mapped[NUM_BACK_BUFFERS]; + VkBuffer index_buffers[NUM_BACK_BUFFERS]; + VmaAllocation index_allocs[NUM_BACK_BUFFERS]; + void *ib_mapped[NUM_BACK_BUFFERS]; // Font atlas - VkImage font_image; - VmaAllocation font_alloc; - VkImageView font_view; - GlyphInfo glyphs[GLYPH_COUNT]; - F32 font_atlas_size; - F32 font_line_height; + VkImage font_image; + VmaAllocation font_alloc; + VkImageView font_view; + GlyphInfo glyphs[GLYPH_COUNT]; + F32 font_atlas_size; + F32 font_line_height; // Icon atlas - VkImage icon_image; - VmaAllocation icon_alloc; - VkImageView icon_view; + VkImage icon_image; + VmaAllocation icon_alloc; + VkImageView icon_view; // FreeType - FT_Library ft_lib; - FT_Face ft_face; + FT_Library ft_lib; + FT_Face ft_face; // Clear color - F32 clear_r; - F32 clear_g; - F32 clear_b; + F32 clear_r; + F32 clear_g; + F32 clear_b; #ifdef _DEBUG VkDebugUtilsMessengerEXT debug_messenger; @@ -169,12 +169,12 @@ struct Renderer { #ifdef _DEBUG static VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT severity, - VkDebugUtilsMessageTypeFlagsEXT type, + VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *data, - void *user_data) -{ - (void)type; (void)user_data; + void *user_data) { + (void)type; + (void)user_data; if (severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { OutputDebugStringA("VK: "); OutputDebugStringA(data->pMessage); @@ -188,27 +188,27 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( // Helper: begin/end one-shot command buffer static VkCommandBuffer begin_one_shot(Renderer *r) { - VkCommandBufferAllocateInfo ai = {0}; - ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - ai.commandPool = r->command_pool; - ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - ai.commandBufferCount = 1; + VkCommandBufferAllocateInfo ai = { 0 }; + ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + ai.commandPool = r->command_pool; + ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + ai.commandBufferCount = 1; VkCommandBuffer cb; vkAllocateCommandBuffers(r->device, &ai, &cb); - VkCommandBufferBeginInfo bi = {0}; - bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkCommandBufferBeginInfo bi = { 0 }; + bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(cb, &bi); return cb; } static void end_one_shot(Renderer *r, VkCommandBuffer cb) { vkEndCommandBuffer(cb); - VkSubmitInfo si = {0}; - si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + VkSubmitInfo si = { 0 }; + si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.commandBufferCount = 1; - si.pCommandBuffers = &cb; + si.pCommandBuffers = &cb; vkQueueSubmit(r->graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(r->graphics_queue); vkFreeCommandBuffers(r->device, r->command_pool, 1, &cb); @@ -218,24 +218,23 @@ static void end_one_shot(Renderer *r, VkCommandBuffer cb) { // Helper: transition image layout static void transition_image(VkCommandBuffer cb, VkImage image, - VkImageLayout old_layout, VkImageLayout new_layout, - VkAccessFlags src_access, VkAccessFlags dst_access, - VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage) -{ - VkImageMemoryBarrier barrier = {0}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = old_layout; - barrier.newLayout = new_layout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; + VkImageLayout old_layout, VkImageLayout new_layout, + VkAccessFlags src_access, VkAccessFlags dst_access, + VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage) { + VkImageMemoryBarrier barrier = { 0 }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.srcAccessMask = src_access; - barrier.dstAccessMask = dst_access; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = src_access; + barrier.dstAccessMask = dst_access; vkCmdPipelineBarrier(cb, src_stage, dst_stage, 0, 0, NULL, 0, NULL, 1, &barrier); } @@ -243,13 +242,13 @@ static void transition_image(VkCommandBuffer cb, VkImage image, // Vulkan infrastructure static B32 create_instance(Renderer *r) { - VkApplicationInfo app_info = {0}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pApplicationName = "autosample"; + VkApplicationInfo app_info = { 0 }; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "autosample"; app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); - app_info.pEngineName = "autosample"; - app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); - app_info.apiVersion = VK_API_VERSION_1_2; + app_info.pEngineName = "autosample"; + app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); + app_info.apiVersion = VK_API_VERSION_1_2; const char *extensions[] = { VK_KHR_SURFACE_EXTENSION_NAME, @@ -265,27 +264,24 @@ static B32 create_instance(Renderer *r) { #endif }; - VkInstanceCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - ci.pApplicationInfo = &app_info; - ci.enabledExtensionCount = ArrayCount(extensions); + VkInstanceCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + ci.pApplicationInfo = &app_info; + ci.enabledExtensionCount = ArrayCount(extensions); ci.ppEnabledExtensionNames = extensions; - ci.enabledLayerCount = ArrayCount(layers); - ci.ppEnabledLayerNames = layers; + ci.enabledLayerCount = ArrayCount(layers); + ci.ppEnabledLayerNames = layers; if (vkCreateInstance(&ci, NULL, &r->instance) != VK_SUCCESS) return 0; #ifdef _DEBUG { - VkDebugUtilsMessengerCreateInfoEXT dbg = {0}; - dbg.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - dbg.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - dbg.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - dbg.pfnUserCallback = vk_debug_callback; + VkDebugUtilsMessengerCreateInfoEXT dbg = { 0 }; + dbg.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + dbg.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + dbg.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + dbg.pfnUserCallback = vk_debug_callback; PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(r->instance, "vkCreateDebugUtilsMessengerEXT"); @@ -297,10 +293,10 @@ static B32 create_instance(Renderer *r) { } static B32 create_surface(Renderer *r) { - VkWin32SurfaceCreateInfoKHR ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - ci.hinstance = GetModuleHandle(NULL); - ci.hwnd = r->hwnd; + VkWin32SurfaceCreateInfoKHR ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + ci.hinstance = GetModuleHandle(NULL); + ci.hwnd = r->hwnd; return vkCreateWin32SurfaceKHR(r->instance, &ci, NULL, &r->surface) == VK_SUCCESS; } @@ -345,20 +341,20 @@ static B32 find_queue_family(Renderer *r) { } static B32 create_device(Renderer *r) { - float priority = 1.0f; - VkDeviceQueueCreateInfo queue_ci = {0}; - queue_ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_ci.queueFamilyIndex = r->queue_family; - queue_ci.queueCount = 1; - queue_ci.pQueuePriorities = &priority; + float priority = 1.0f; + VkDeviceQueueCreateInfo queue_ci = { 0 }; + queue_ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_ci.queueFamilyIndex = r->queue_family; + queue_ci.queueCount = 1; + queue_ci.pQueuePriorities = &priority; const char *extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - VkDeviceCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - ci.queueCreateInfoCount = 1; - ci.pQueueCreateInfos = &queue_ci; - ci.enabledExtensionCount = 1; + VkDeviceCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + ci.queueCreateInfoCount = 1; + ci.pQueueCreateInfos = &queue_ci; + ci.enabledExtensionCount = 1; ci.ppEnabledExtensionNames = extensions; if (vkCreateDevice(r->physical_device, &ci, NULL, &r->device) != VK_SUCCESS) @@ -369,11 +365,11 @@ static B32 create_device(Renderer *r) { } static B32 create_vma(Renderer *r) { - VmaAllocatorCreateInfo ci = {0}; - ci.physicalDevice = r->physical_device; - ci.device = r->device; - ci.instance = r->instance; - ci.vulkanApiVersion = VK_API_VERSION_1_2; + VmaAllocatorCreateInfo ci = { 0 }; + ci.physicalDevice = r->physical_device; + ci.device = r->device; + ci.instance = r->instance; + ci.vulkanApiVersion = VK_API_VERSION_1_2; return vmaCreateAllocator(&ci, &r->allocator) == VK_SUCCESS; } @@ -417,7 +413,7 @@ static B32 create_swap_chain(Renderer *r) { if (caps.currentExtent.width != 0xFFFFFFFF) { extent = caps.currentExtent; } else { - extent.width = (U32)r->width; + extent.width = (U32)r->width; extent.height = (U32)r->height; } @@ -426,21 +422,21 @@ static B32 create_swap_chain(Renderer *r) { if (caps.maxImageCount > 0 && image_count > caps.maxImageCount) image_count = caps.maxImageCount; - VkSwapchainCreateInfoKHR ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - ci.surface = r->surface; - ci.minImageCount = image_count; - ci.imageFormat = chosen.format; - ci.imageColorSpace = chosen.colorSpace; - ci.imageExtent = extent; - ci.imageArrayLayers = 1; - ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - ci.preTransform = caps.currentTransform; - ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - ci.presentMode = present_mode; - ci.clipped = VK_TRUE; - ci.oldSwapchain = r->swap_chain; // for recreation + VkSwapchainCreateInfoKHR ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + ci.surface = r->surface; + ci.minImageCount = image_count; + ci.imageFormat = chosen.format; + ci.imageColorSpace = chosen.colorSpace; + ci.imageExtent = extent; + ci.imageArrayLayers = 1; + ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + ci.preTransform = caps.currentTransform; + ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + ci.presentMode = present_mode; + ci.clipped = VK_TRUE; + ci.oldSwapchain = r->swap_chain; // for recreation if (vkCreateSwapchainKHR(r->device, &ci, NULL, &r->swap_chain) != VK_SUCCESS) return 0; @@ -451,11 +447,11 @@ static B32 create_swap_chain(Renderer *r) { // Create image views for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) { - VkImageViewCreateInfo vi = {0}; - vi.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - vi.image = r->swap_chain_images[i]; - vi.viewType = VK_IMAGE_VIEW_TYPE_2D; - vi.format = r->swap_chain_format; + VkImageViewCreateInfo vi = { 0 }; + vi.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + vi.image = r->swap_chain_images[i]; + vi.viewType = VK_IMAGE_VIEW_TYPE_2D; + vi.format = r->swap_chain_format; vi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vi.subresourceRange.levelCount = 1; vi.subresourceRange.layerCount = 1; @@ -463,61 +459,61 @@ static B32 create_swap_chain(Renderer *r) { return 0; } - r->width = (S32)extent.width; + r->width = (S32)extent.width; r->height = (S32)extent.height; return 1; } static B32 create_render_pass(Renderer *r) { - VkAttachmentDescription color_attach = {0}; - color_attach.format = r->swap_chain_format; - color_attach.samples = VK_SAMPLE_COUNT_1_BIT; - color_attach.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attach.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attach.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - color_attach.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color_attach.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - color_attach.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentDescription color_attach = { 0 }; + color_attach.format = r->swap_chain_format; + color_attach.samples = VK_SAMPLE_COUNT_1_BIT; + color_attach.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attach.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attach.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attach.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attach.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attach.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_ref = {0}; - color_ref.attachment = 0; - color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference color_ref = { 0 }; + color_ref.attachment = 0; + color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {0}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + VkSubpassDescription subpass = { 0 }; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_ref; + subpass.pColorAttachments = &color_ref; - VkSubpassDependency dep = {0}; - dep.srcSubpass = VK_SUBPASS_EXTERNAL; - dep.dstSubpass = 0; - dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.srcAccessMask = 0; - dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkSubpassDependency dep = { 0 }; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - VkRenderPassCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - ci.attachmentCount = 1; - ci.pAttachments = &color_attach; - ci.subpassCount = 1; - ci.pSubpasses = &subpass; - ci.dependencyCount = 1; - ci.pDependencies = &dep; + VkRenderPassCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + ci.attachmentCount = 1; + ci.pAttachments = &color_attach; + ci.subpassCount = 1; + ci.pSubpasses = &subpass; + ci.dependencyCount = 1; + ci.pDependencies = &dep; return vkCreateRenderPass(r->device, &ci, NULL, &r->render_pass) == VK_SUCCESS; } static B32 create_framebuffers(Renderer *r) { for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) { - VkFramebufferCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - ci.renderPass = r->render_pass; - ci.attachmentCount = 1; - ci.pAttachments = &r->swap_chain_views[i]; - ci.width = (U32)r->width; - ci.height = (U32)r->height; - ci.layers = 1; + VkFramebufferCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + ci.renderPass = r->render_pass; + ci.attachmentCount = 1; + ci.pAttachments = &r->swap_chain_views[i]; + ci.width = (U32)r->width; + ci.height = (U32)r->height; + ci.layers = 1; if (vkCreateFramebuffer(r->device, &ci, NULL, &r->framebuffers[i]) != VK_SUCCESS) return 0; } @@ -525,18 +521,18 @@ static B32 create_framebuffers(Renderer *r) { } static B32 create_command_resources(Renderer *r) { - VkCommandPoolCreateInfo pool_ci = {0}; - pool_ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - pool_ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - pool_ci.queueFamilyIndex = r->queue_family; + VkCommandPoolCreateInfo pool_ci = { 0 }; + pool_ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_ci.queueFamilyIndex = r->queue_family; if (vkCreateCommandPool(r->device, &pool_ci, NULL, &r->command_pool) != VK_SUCCESS) return 0; - VkCommandBufferAllocateInfo alloc_info = {0}; - alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - alloc_info.commandPool = r->command_pool; - alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = NUM_BACK_BUFFERS; + VkCommandBufferAllocateInfo alloc_info = { 0 }; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = r->command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = NUM_BACK_BUFFERS; VkCommandBuffer cbs[NUM_BACK_BUFFERS]; if (vkAllocateCommandBuffers(r->device, &alloc_info, cbs) != VK_SUCCESS) @@ -545,16 +541,16 @@ static B32 create_command_resources(Renderer *r) { for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { r->frames[i].command_buffer = cbs[i]; - VkFenceCreateInfo fence_ci = {0}; - fence_ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fence_ci.flags = VK_FENCE_CREATE_SIGNALED_BIT; + VkFenceCreateInfo fence_ci = { 0 }; + fence_ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_ci.flags = VK_FENCE_CREATE_SIGNALED_BIT; if (vkCreateFence(r->device, &fence_ci, NULL, &r->frames[i].fence) != VK_SUCCESS) return 0; } for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { - VkSemaphoreCreateInfo sem_ci = {0}; - sem_ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkSemaphoreCreateInfo sem_ci = { 0 }; + sem_ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; if (vkCreateSemaphore(r->device, &sem_ci, NULL, &r->image_available_sema[i]) != VK_SUCCESS) return 0; if (vkCreateSemaphore(r->device, &sem_ci, NULL, &r->render_finished_sema[i]) != VK_SUCCESS) @@ -565,51 +561,51 @@ static B32 create_command_resources(Renderer *r) { } static B32 create_sampler(Renderer *r) { - VkSamplerCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - ci.magFilter = VK_FILTER_LINEAR; - ci.minFilter = VK_FILTER_LINEAR; - ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - ci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - ci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + VkSamplerCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + ci.magFilter = VK_FILTER_LINEAR; + ci.minFilter = VK_FILTER_LINEAR; + ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + ci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + ci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; return vkCreateSampler(r->device, &ci, NULL, &r->sampler) == VK_SUCCESS; } static B32 create_descriptor_resources(Renderer *r) { // Descriptor set layout: single combined image sampler at binding 0 - VkDescriptorSetLayoutBinding binding = {0}; - binding.binding = 0; - binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - binding.descriptorCount = 1; - binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutBinding binding = { 0 }; + binding.binding = 0; + binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding.descriptorCount = 1; + binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - VkDescriptorSetLayoutCreateInfo layout_ci = {0}; - layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layout_ci.bindingCount = 1; - layout_ci.pBindings = &binding; + VkDescriptorSetLayoutCreateInfo layout_ci = { 0 }; + layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_ci.bindingCount = 1; + layout_ci.pBindings = &binding; if (vkCreateDescriptorSetLayout(r->device, &layout_ci, NULL, &r->descriptor_set_layout) != VK_SUCCESS) return 0; // Descriptor pool: 2 sets (font + icon) - VkDescriptorPoolSize pool_size = {0}; - pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - pool_size.descriptorCount = 2; + VkDescriptorPoolSize pool_size = { 0 }; + pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_size.descriptorCount = 2; - VkDescriptorPoolCreateInfo pool_ci = {0}; - pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_ci.maxSets = 2; - pool_ci.poolSizeCount = 1; - pool_ci.pPoolSizes = &pool_size; + VkDescriptorPoolCreateInfo pool_ci = { 0 }; + pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_ci.maxSets = 2; + pool_ci.poolSizeCount = 1; + pool_ci.pPoolSizes = &pool_size; if (vkCreateDescriptorPool(r->device, &pool_ci, NULL, &r->descriptor_pool) != VK_SUCCESS) return 0; // Allocate font descriptor set - VkDescriptorSetAllocateInfo alloc_info = {0}; - alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = r->descriptor_pool; - alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &r->descriptor_set_layout; + VkDescriptorSetAllocateInfo alloc_info = { 0 }; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = r->descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &r->descriptor_set_layout; if (vkAllocateDescriptorSets(r->device, &alloc_info, &r->font_descriptor_set) != VK_SUCCESS) return 0; @@ -617,11 +613,11 @@ static B32 create_descriptor_resources(Renderer *r) { } static VkShaderModule create_shader_module(Renderer *r, const U32 *code, size_t size) { - VkShaderModuleCreateInfo ci = {0}; - ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - ci.codeSize = size; - ci.pCode = code; - VkShaderModule module = VK_NULL_HANDLE; + VkShaderModuleCreateInfo ci = { 0 }; + ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + ci.codeSize = size; + ci.pCode = code; + VkShaderModule module = VK_NULL_HANDLE; vkCreateShaderModule(r->device, &ci, NULL, &module); return module; } @@ -631,119 +627,145 @@ static B32 create_pipeline(Renderer *r) { VkShaderModule frag_module = create_shader_module(r, ui_frag_spv, sizeof(ui_frag_spv)); if (!vert_module || !frag_module) return 0; - VkPipelineShaderStageCreateInfo stages[2] = {{0}, {0}}; - stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - stages[0].module = vert_module; - stages[0].pName = "main"; - stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stages[1].module = frag_module; - stages[1].pName = "main"; + VkPipelineShaderStageCreateInfo stages[2] = { { 0 }, { 0 } }; + stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stages[0].module = vert_module; + stages[0].pName = "main"; + stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stages[1].module = frag_module; + stages[1].pName = "main"; // Vertex input - VkVertexInputBindingDescription binding = {0}; - binding.binding = 0; - binding.stride = sizeof(UIVertex); - binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + VkVertexInputBindingDescription binding = { 0 }; + binding.binding = 0; + binding.stride = sizeof(UIVertex); + binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkVertexInputAttributeDescription attrs[9] = {}; - attrs[0].location = 0; attrs[0].binding = 0; attrs[0].format = VK_FORMAT_R32G32_SFLOAT; attrs[0].offset = offsetof(UIVertex, pos); - attrs[1].location = 1; attrs[1].binding = 0; attrs[1].format = VK_FORMAT_R32G32_SFLOAT; attrs[1].offset = offsetof(UIVertex, uv); - attrs[2].location = 2; attrs[2].binding = 0; attrs[2].format = VK_FORMAT_R32G32B32A32_SFLOAT; attrs[2].offset = offsetof(UIVertex, col); - attrs[3].location = 3; attrs[3].binding = 0; attrs[3].format = VK_FORMAT_R32G32_SFLOAT; attrs[3].offset = offsetof(UIVertex, rect_min); - attrs[4].location = 4; attrs[4].binding = 0; attrs[4].format = VK_FORMAT_R32G32_SFLOAT; attrs[4].offset = offsetof(UIVertex, rect_max); - attrs[5].location = 5; attrs[5].binding = 0; attrs[5].format = VK_FORMAT_R32G32B32A32_SFLOAT; attrs[5].offset = offsetof(UIVertex, corner_radii); - attrs[6].location = 6; attrs[6].binding = 0; attrs[6].format = VK_FORMAT_R32_SFLOAT; attrs[6].offset = offsetof(UIVertex, border_thickness); - attrs[7].location = 7; attrs[7].binding = 0; attrs[7].format = VK_FORMAT_R32_SFLOAT; attrs[7].offset = offsetof(UIVertex, softness); - attrs[8].location = 8; attrs[8].binding = 0; attrs[8].format = VK_FORMAT_R32_SFLOAT; attrs[8].offset = offsetof(UIVertex, mode); + attrs[0].location = 0; + attrs[0].binding = 0; + attrs[0].format = VK_FORMAT_R32G32_SFLOAT; + attrs[0].offset = offsetof(UIVertex, pos); + attrs[1].location = 1; + attrs[1].binding = 0; + attrs[1].format = VK_FORMAT_R32G32_SFLOAT; + attrs[1].offset = offsetof(UIVertex, uv); + attrs[2].location = 2; + attrs[2].binding = 0; + attrs[2].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attrs[2].offset = offsetof(UIVertex, col); + attrs[3].location = 3; + attrs[3].binding = 0; + attrs[3].format = VK_FORMAT_R32G32_SFLOAT; + attrs[3].offset = offsetof(UIVertex, rect_min); + attrs[4].location = 4; + attrs[4].binding = 0; + attrs[4].format = VK_FORMAT_R32G32_SFLOAT; + attrs[4].offset = offsetof(UIVertex, rect_max); + attrs[5].location = 5; + attrs[5].binding = 0; + attrs[5].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attrs[5].offset = offsetof(UIVertex, corner_radii); + attrs[6].location = 6; + attrs[6].binding = 0; + attrs[6].format = VK_FORMAT_R32_SFLOAT; + attrs[6].offset = offsetof(UIVertex, border_thickness); + attrs[7].location = 7; + attrs[7].binding = 0; + attrs[7].format = VK_FORMAT_R32_SFLOAT; + attrs[7].offset = offsetof(UIVertex, softness); + attrs[8].location = 8; + attrs[8].binding = 0; + attrs[8].format = VK_FORMAT_R32_SFLOAT; + attrs[8].offset = offsetof(UIVertex, mode); - VkPipelineVertexInputStateCreateInfo vertex_input = {0}; - vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertex_input.vertexBindingDescriptionCount = 1; - vertex_input.pVertexBindingDescriptions = &binding; - vertex_input.vertexAttributeDescriptionCount = 9; - vertex_input.pVertexAttributeDescriptions = attrs; + VkPipelineVertexInputStateCreateInfo vertex_input = { 0 }; + vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input.vertexBindingDescriptionCount = 1; + vertex_input.pVertexBindingDescriptions = &binding; + vertex_input.vertexAttributeDescriptionCount = 9; + vertex_input.pVertexAttributeDescriptions = attrs; - VkPipelineInputAssemblyStateCreateInfo input_assembly = {0}; - input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkPipelineInputAssemblyStateCreateInfo input_assembly = { 0 }; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // Dynamic viewport/scissor - VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamic_state = {0}; - dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = 2; - dynamic_state.pDynamicStates = dynamic_states; + VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamic_state = { 0 }; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = 2; + dynamic_state.pDynamicStates = dynamic_states; - VkPipelineViewportStateCreateInfo viewport_state = {0}; - viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewport_state.viewportCount = 1; - viewport_state.scissorCount = 1; + VkPipelineViewportStateCreateInfo viewport_state = { 0 }; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; - VkPipelineRasterizationStateCreateInfo rasterizer = {0}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + VkPipelineRasterizationStateCreateInfo rasterizer = { 0 }; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - VkPipelineMultisampleStateCreateInfo multisample = {0}; - multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + VkPipelineMultisampleStateCreateInfo multisample = { 0 }; + multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineColorBlendAttachmentState blend_attach = {0}; - blend_attach.blendEnable = VK_TRUE; - blend_attach.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blend_attach.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blend_attach.colorBlendOp = VK_BLEND_OP_ADD; - blend_attach.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - blend_attach.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blend_attach.alphaBlendOp = VK_BLEND_OP_ADD; - blend_attach.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT - | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + VkPipelineColorBlendAttachmentState blend_attach = { 0 }; + blend_attach.blendEnable = VK_TRUE; + blend_attach.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend_attach.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend_attach.colorBlendOp = VK_BLEND_OP_ADD; + blend_attach.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blend_attach.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend_attach.alphaBlendOp = VK_BLEND_OP_ADD; + blend_attach.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - VkPipelineColorBlendStateCreateInfo blend = {0}; - blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - blend.attachmentCount = 1; - blend.pAttachments = &blend_attach; + VkPipelineColorBlendStateCreateInfo blend = { 0 }; + blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend.attachmentCount = 1; + blend.pAttachments = &blend_attach; - VkPipelineDepthStencilStateCreateInfo depth_stencil = {0}; - depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + VkPipelineDepthStencilStateCreateInfo depth_stencil = { 0 }; + depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; // Pipeline layout: push constants + 1 descriptor set - VkPushConstantRange push_range = {0}; - push_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_range.offset = 0; - push_range.size = 4 * sizeof(float); // viewport_size + padding + VkPushConstantRange push_range = { 0 }; + push_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_range.offset = 0; + push_range.size = 4 * sizeof(float); // viewport_size + padding - VkPipelineLayoutCreateInfo layout_ci = {0}; - layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_ci.setLayoutCount = 1; - layout_ci.pSetLayouts = &r->descriptor_set_layout; - layout_ci.pushConstantRangeCount = 1; - layout_ci.pPushConstantRanges = &push_range; + VkPipelineLayoutCreateInfo layout_ci = { 0 }; + layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_ci.setLayoutCount = 1; + layout_ci.pSetLayouts = &r->descriptor_set_layout; + layout_ci.pushConstantRangeCount = 1; + layout_ci.pPushConstantRanges = &push_range; if (vkCreatePipelineLayout(r->device, &layout_ci, NULL, &r->pipeline_layout) != VK_SUCCESS) { vkDestroyShaderModule(r->device, vert_module, NULL); vkDestroyShaderModule(r->device, frag_module, NULL); return 0; } - VkGraphicsPipelineCreateInfo pipeline_ci = {0}; - pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipeline_ci.stageCount = 2; - pipeline_ci.pStages = stages; - pipeline_ci.pVertexInputState = &vertex_input; - pipeline_ci.pInputAssemblyState = &input_assembly; - pipeline_ci.pViewportState = &viewport_state; - pipeline_ci.pRasterizationState = &rasterizer; - pipeline_ci.pMultisampleState = &multisample; - pipeline_ci.pDepthStencilState = &depth_stencil; - pipeline_ci.pColorBlendState = &blend; - pipeline_ci.pDynamicState = &dynamic_state; - pipeline_ci.layout = r->pipeline_layout; - pipeline_ci.renderPass = r->render_pass; - pipeline_ci.subpass = 0; + VkGraphicsPipelineCreateInfo pipeline_ci = { 0 }; + pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_ci.stageCount = 2; + pipeline_ci.pStages = stages; + pipeline_ci.pVertexInputState = &vertex_input; + pipeline_ci.pInputAssemblyState = &input_assembly; + pipeline_ci.pViewportState = &viewport_state; + pipeline_ci.pRasterizationState = &rasterizer; + pipeline_ci.pMultisampleState = &multisample; + pipeline_ci.pDepthStencilState = &depth_stencil; + pipeline_ci.pColorBlendState = &blend; + pipeline_ci.pDynamicState = &dynamic_state; + pipeline_ci.layout = r->pipeline_layout; + pipeline_ci.renderPass = r->render_pass; + pipeline_ci.subpass = 0; VkResult result = vkCreateGraphicsPipelines(r->device, VK_NULL_HANDLE, 1, &pipeline_ci, NULL, &r->pipeline); @@ -755,25 +777,25 @@ static B32 create_pipeline(Renderer *r) { static B32 create_ui_buffers(Renderer *r) { for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { - VkBufferCreateInfo buf_ci = {0}; - buf_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buf_ci.size = MAX_VERTICES * sizeof(UIVertex); - buf_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + VkBufferCreateInfo buf_ci = { 0 }; + buf_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_ci.size = MAX_VERTICES * sizeof(UIVertex); + buf_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; - alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + VmaAllocationCreateInfo alloc_ci = { 0 }; + alloc_ci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; VmaAllocationInfo alloc_info; if (vmaCreateBuffer(r->allocator, &buf_ci, &alloc_ci, - &r->vertex_buffers[i], &r->vertex_allocs[i], &alloc_info) != VK_SUCCESS) + &r->vertex_buffers[i], &r->vertex_allocs[i], &alloc_info) != VK_SUCCESS) return 0; r->vb_mapped[i] = alloc_info.pMappedData; - buf_ci.size = MAX_INDICES * sizeof(U32); + buf_ci.size = MAX_INDICES * sizeof(U32); buf_ci.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; if (vmaCreateBuffer(r->allocator, &buf_ci, &alloc_ci, - &r->index_buffers[i], &r->index_allocs[i], &alloc_info) != VK_SUCCESS) + &r->index_buffers[i], &r->index_allocs[i], &alloc_info) != VK_SUCCESS) return 0; r->ib_mapped[i] = alloc_info.pMappedData; } @@ -784,18 +806,18 @@ static B32 create_ui_buffers(Renderer *r) { // Helper: update a descriptor set to point to an image view static void update_descriptor_set(Renderer *r, VkDescriptorSet set, VkImageView view) { - VkDescriptorImageInfo img_info = {0}; - img_info.sampler = r->sampler; - img_info.imageView = view; - img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkDescriptorImageInfo img_info = { 0 }; + img_info.sampler = r->sampler; + img_info.imageView = view; + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkWriteDescriptorSet write = {0}; - write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write.dstSet = set; - write.dstBinding = 0; - write.descriptorCount = 1; - write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write.pImageInfo = &img_info; + VkWriteDescriptorSet write = { 0 }; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.dstSet = set; + write.dstBinding = 0; + write.descriptorCount = 1; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.pImageInfo = &img_info; vkUpdateDescriptorSets(r->device, 1, &write, 0, NULL); } @@ -808,7 +830,7 @@ static void init_freetype(Renderer *r) { } static B32 create_font_atlas(Renderer *r, F32 font_size) { - S32 pixel_size = (S32)(font_size + 0.5f); + S32 pixel_size = (S32)(font_size + 0.5f); r->font_atlas_size = font_size; FT_Set_Pixel_Sizes(r->ft_face, 0, pixel_size); @@ -818,18 +840,18 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { S32 pen_x = 1, pen_y = 1; S32 row_height = 0; - F32 ascender = (F32)(r->ft_face->size->metrics.ascender >> 6); + F32 ascender = (F32)(r->ft_face->size->metrics.ascender >> 6); for (S32 i = 0; i < GLYPH_COUNT; i++) { char ch = (char)(GLYPH_FIRST + i); if (FT_Load_Char(r->ft_face, ch, FT_LOAD_RENDER)) continue; - FT_GlyphSlot g = r->ft_face->glyph; - S32 bw = (S32)g->bitmap.width; - S32 bh = (S32)g->bitmap.rows; - S32 pad = 2; - S32 cell_w = bw + pad; - S32 cell_h = (S32)r->font_line_height + pad; + FT_GlyphSlot g = r->ft_face->glyph; + S32 bw = (S32)g->bitmap.width; + S32 bh = (S32)g->bitmap.rows; + S32 pad = 2; + S32 cell_w = bw + pad; + S32 cell_h = (S32)r->font_line_height + pad; if (pen_x + cell_w >= FONT_ATLAS_W) { pen_x = 1; @@ -849,12 +871,12 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { } } - r->glyphs[i].u0 = (F32)pen_x / (F32)FONT_ATLAS_W; - r->glyphs[i].v0 = (F32)pen_y / (F32)FONT_ATLAS_H; - r->glyphs[i].u1 = (F32)(pen_x + cell_w) / (F32)FONT_ATLAS_W; - r->glyphs[i].v1 = (F32)(pen_y + cell_h) / (F32)FONT_ATLAS_H; - r->glyphs[i].w = (F32)cell_w; - r->glyphs[i].h = (F32)cell_h; + r->glyphs[i].u0 = (F32)pen_x / (F32)FONT_ATLAS_W; + r->glyphs[i].v0 = (F32)pen_y / (F32)FONT_ATLAS_H; + r->glyphs[i].u1 = (F32)(pen_x + cell_w) / (F32)FONT_ATLAS_W; + r->glyphs[i].v1 = (F32)(pen_y + cell_h) / (F32)FONT_ATLAS_H; + r->glyphs[i].w = (F32)cell_w; + r->glyphs[i].h = (F32)cell_h; r->glyphs[i].x_advance = (F32)(g->advance.x >> 6); if (cell_h > row_height) row_height = cell_h; @@ -862,21 +884,21 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { } // Create Vulkan image - VkImageCreateInfo img_ci = {0}; - img_ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - img_ci.imageType = VK_IMAGE_TYPE_2D; - img_ci.format = VK_FORMAT_R8_UNORM; - img_ci.extent.width = FONT_ATLAS_W; - img_ci.extent.height = FONT_ATLAS_H; - img_ci.extent.depth = 1; - img_ci.mipLevels = 1; - img_ci.arrayLayers = 1; - img_ci.samples = VK_SAMPLE_COUNT_1_BIT; - img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; - img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + VkImageCreateInfo img_ci = { 0 }; + img_ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + img_ci.imageType = VK_IMAGE_TYPE_2D; + img_ci.format = VK_FORMAT_R8_UNORM; + img_ci.extent.width = FONT_ATLAS_W; + img_ci.extent.height = FONT_ATLAS_H; + img_ci.extent.depth = 1; + img_ci.mipLevels = 1; + img_ci.arrayLayers = 1; + img_ci.samples = VK_SAMPLE_COUNT_1_BIT; + img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; + img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; + VmaAllocationCreateInfo alloc_ci = { 0 }; + alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; if (vmaCreateImage(r->allocator, &img_ci, &alloc_ci, &r->font_image, &r->font_alloc, NULL) != VK_SUCCESS) { free(atlas_data); @@ -884,17 +906,17 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { } // Staging buffer - VkBufferCreateInfo staging_ci = {0}; - staging_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - staging_ci.size = FONT_ATLAS_W * FONT_ATLAS_H; - staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkBufferCreateInfo staging_ci = { 0 }; + staging_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + staging_ci.size = FONT_ATLAS_W * FONT_ATLAS_H; + staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo staging_alloc_ci = {0}; - staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; - staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + VmaAllocationCreateInfo staging_alloc_ci = { 0 }; + staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; + staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - VkBuffer staging_buf; - VmaAllocation staging_alloc; + VkBuffer staging_buf; + VmaAllocation staging_alloc; VmaAllocationInfo staging_info; vmaCreateBuffer(r->allocator, &staging_ci, &staging_alloc_ci, &staging_buf, &staging_alloc, &staging_info); memcpy(staging_info.pMappedData, atlas_data, FONT_ATLAS_W * FONT_ATLAS_H); @@ -904,33 +926,33 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { VkCommandBuffer cb = begin_one_shot(r); transition_image(cb, r->font_image, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkBufferImageCopy region = {0}; + VkBufferImageCopy region = { 0 }; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; - region.imageExtent.width = FONT_ATLAS_W; - region.imageExtent.height = FONT_ATLAS_H; - region.imageExtent.depth = 1; + region.imageExtent.width = FONT_ATLAS_W; + region.imageExtent.height = FONT_ATLAS_H; + region.imageExtent.depth = 1; vkCmdCopyBufferToImage(cb, staging_buf, r->font_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); transition_image(cb, r->font_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); end_one_shot(r, cb); vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc); // Image view - VkImageViewCreateInfo view_ci = {0}; - view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_ci.image = r->font_image; - view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_ci.format = VK_FORMAT_R8_UNORM; + VkImageViewCreateInfo view_ci = { 0 }; + view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_ci.image = r->font_image; + view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_ci.format = VK_FORMAT_R8_UNORM; view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_ci.subresourceRange.levelCount = 1; view_ci.subresourceRange.layerCount = 1; @@ -972,63 +994,87 @@ typedef struct DrawBatch { } DrawBatch; static void emit_quad(DrawBatch *batch, - F32 x0, F32 y0, F32 x1, F32 y1, - F32 u0, F32 v0, F32 u1, F32 v1, - F32 cr, F32 cg, F32 cb, F32 ca, - F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y, - F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, - F32 border_thickness, F32 softness, F32 mode) -{ + F32 x0, F32 y0, F32 x1, F32 y1, + F32 u0, F32 v0, F32 u1, F32 v1, + F32 cr, F32 cg, F32 cb, F32 ca, + F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y, + F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, + F32 border_thickness, F32 softness, F32 mode) { if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) return; - U32 base = batch->vertex_count; - UIVertex *v = &batch->vertices[base]; + U32 base = batch->vertex_count; + UIVertex *v = &batch->vertices[base]; F32 px0 = x0, py0 = y0, px1 = x1, py1 = y1; if (mode < 0.5f) { F32 pad = softness + 1.0f; - px0 -= pad; py0 -= pad; px1 += pad; py1 += pad; + px0 -= pad; + py0 -= pad; + px1 += pad; + py1 += pad; } - v[0].pos[0] = px0; v[0].pos[1] = py0; v[0].uv[0] = u0; v[0].uv[1] = v0; - v[1].pos[0] = px1; v[1].pos[1] = py0; v[1].uv[0] = u1; v[1].uv[1] = v0; - v[2].pos[0] = px1; v[2].pos[1] = py1; v[2].uv[0] = u1; v[2].uv[1] = v1; - v[3].pos[0] = px0; v[3].pos[1] = py1; v[3].uv[0] = u0; v[3].uv[1] = v1; + v[0].pos[0] = px0; + v[0].pos[1] = py0; + v[0].uv[0] = u0; + v[0].uv[1] = v0; + v[1].pos[0] = px1; + v[1].pos[1] = py0; + v[1].uv[0] = u1; + v[1].uv[1] = v0; + v[2].pos[0] = px1; + v[2].pos[1] = py1; + v[2].uv[0] = u1; + v[2].uv[1] = v1; + v[3].pos[0] = px0; + v[3].pos[1] = py1; + v[3].uv[0] = u0; + v[3].uv[1] = v1; for (S32 i = 0; i < 4; i++) { - v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca; - v[i].rect_min[0] = rmin_x; v[i].rect_min[1] = rmin_y; - v[i].rect_max[0] = rmax_x; v[i].rect_max[1] = rmax_y; - v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr; - v[i].corner_radii[2] = cr_br; v[i].corner_radii[3] = cr_bl; + v[i].col[0] = cr; + v[i].col[1] = cg; + v[i].col[2] = cb; + v[i].col[3] = ca; + v[i].rect_min[0] = rmin_x; + v[i].rect_min[1] = rmin_y; + v[i].rect_max[0] = rmax_x; + v[i].rect_max[1] = rmax_y; + v[i].corner_radii[0] = cr_tl; + v[i].corner_radii[1] = cr_tr; + v[i].corner_radii[2] = cr_br; + v[i].corner_radii[3] = cr_bl; v[i].border_thickness = border_thickness; - v[i].softness = softness; - v[i].mode = mode; + v[i].softness = softness; + v[i].mode = mode; } U32 *idx = &batch->indices[batch->index_count]; - idx[0] = base; idx[1] = base + 1; idx[2] = base + 2; - idx[3] = base; idx[4] = base + 2; idx[5] = base + 3; + idx[0] = base; + idx[1] = base + 1; + idx[2] = base + 2; + idx[3] = base; + idx[4] = base + 2; + idx[5] = base + 3; batch->vertex_count += 4; batch->index_count += 6; } static void emit_quad_rotated(DrawBatch *batch, - F32 x0, F32 y0, F32 x1, F32 y1, - F32 u0, F32 v0, F32 u1, F32 v1, - F32 cr, F32 cg, F32 cb, F32 ca, - F32 angle_rad) -{ + F32 x0, F32 y0, F32 x1, F32 y1, + F32 u0, F32 v0, F32 u1, F32 v1, + F32 cr, F32 cg, F32 cb, F32 ca, + F32 angle_rad) { if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) return; - U32 base = batch->vertex_count; - UIVertex *v = &batch->vertices[base]; + U32 base = batch->vertex_count; + UIVertex *v = &batch->vertices[base]; - F32 cx = (x0 + x1) * 0.5f; - F32 cy = (y0 + y1) * 0.5f; + F32 cx = (x0 + x1) * 0.5f; + F32 cy = (y0 + y1) * 0.5f; F32 cosA = cosf(angle_rad); F32 sinA = sinf(angle_rad); @@ -1037,101 +1083,145 @@ static void emit_quad_rotated(DrawBatch *batch, v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA; v[0].pos[1] = cy + dx0 * sinA + dy0 * cosA; - v[0].uv[0] = u0; v[0].uv[1] = v0; + v[0].uv[0] = u0; + v[0].uv[1] = v0; v[1].pos[0] = cx + dx1 * cosA - dy0 * sinA; v[1].pos[1] = cy + dx1 * sinA + dy0 * cosA; - v[1].uv[0] = u1; v[1].uv[1] = v0; + v[1].uv[0] = u1; + v[1].uv[1] = v0; v[2].pos[0] = cx + dx1 * cosA - dy1 * sinA; v[2].pos[1] = cy + dx1 * sinA + dy1 * cosA; - v[2].uv[0] = u1; v[2].uv[1] = v1; + v[2].uv[0] = u1; + v[2].uv[1] = v1; v[3].pos[0] = cx + dx0 * cosA - dy1 * sinA; v[3].pos[1] = cy + dx0 * sinA + dy1 * cosA; - v[3].uv[0] = u0; v[3].uv[1] = v1; + v[3].uv[0] = u0; + v[3].uv[1] = v1; for (S32 i = 0; i < 4; i++) { - v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca; - v[i].rect_min[0] = 0; v[i].rect_min[1] = 0; - v[i].rect_max[0] = 0; v[i].rect_max[1] = 0; - v[i].corner_radii[0] = 0; v[i].corner_radii[1] = 0; - v[i].corner_radii[2] = 0; v[i].corner_radii[3] = 0; + v[i].col[0] = cr; + v[i].col[1] = cg; + v[i].col[2] = cb; + v[i].col[3] = ca; + v[i].rect_min[0] = 0; + v[i].rect_min[1] = 0; + v[i].rect_max[0] = 0; + v[i].rect_max[1] = 0; + v[i].corner_radii[0] = 0; + v[i].corner_radii[1] = 0; + v[i].corner_radii[2] = 0; + v[i].corner_radii[3] = 0; v[i].border_thickness = 0; - v[i].softness = 0; - v[i].mode = 2.0f; + v[i].softness = 0; + v[i].mode = 2.0f; } U32 *idx = &batch->indices[batch->index_count]; - idx[0] = base; idx[1] = base + 1; idx[2] = base + 2; - idx[3] = base; idx[4] = base + 2; idx[5] = base + 3; + idx[0] = base; + idx[1] = base + 1; + idx[2] = base + 2; + idx[3] = base; + idx[4] = base + 2; + idx[5] = base + 3; batch->vertex_count += 4; batch->index_count += 6; } static void emit_rect(DrawBatch *batch, - F32 x0, F32 y0, F32 x1, F32 y1, - F32 cr, F32 cg, F32 cb, F32 ca, - F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, - F32 border_thickness, F32 softness) -{ + F32 x0, F32 y0, F32 x1, F32 y1, + F32 cr, F32 cg, F32 cb, F32 ca, + F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, + F32 border_thickness, F32 softness) { emit_quad(batch, x0, y0, x1, y1, - 0, 0, 0, 0, - cr, cg, cb, ca, - x0, y0, x1, y1, - cr_tl, cr_tr, cr_br, cr_bl, - border_thickness, softness, 0.0f); + 0, 0, 0, 0, + cr, cg, cb, ca, + x0, y0, x1, y1, + cr_tl, cr_tr, cr_br, cr_bl, + border_thickness, softness, 0.0f); } static void emit_rect_vgradient(DrawBatch *batch, - F32 x0, F32 y0, F32 x1, F32 y1, - F32 tr, F32 tg, F32 tb, F32 ta, - F32 br, F32 bg, F32 bb_, F32 ba, - F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, - F32 softness) -{ + F32 x0, F32 y0, F32 x1, F32 y1, + F32 tr, F32 tg, F32 tb, F32 ta, + F32 br, F32 bg, F32 bb_, F32 ba, + F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl, + F32 softness) { if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) return; - U32 base = batch->vertex_count; - UIVertex *v = &batch->vertices[base]; + U32 base = batch->vertex_count; + UIVertex *v = &batch->vertices[base]; F32 pad = softness + 1.0f; F32 px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad; - v[0].pos[0] = px0; v[0].pos[1] = py0; v[0].uv[0] = 0; v[0].uv[1] = 0; - v[1].pos[0] = px1; v[1].pos[1] = py0; v[1].uv[0] = 0; v[1].uv[1] = 0; - v[2].pos[0] = px1; v[2].pos[1] = py1; v[2].uv[0] = 0; v[2].uv[1] = 0; - v[3].pos[0] = px0; v[3].pos[1] = py1; v[3].uv[0] = 0; v[3].uv[1] = 0; + v[0].pos[0] = px0; + v[0].pos[1] = py0; + v[0].uv[0] = 0; + v[0].uv[1] = 0; + v[1].pos[0] = px1; + v[1].pos[1] = py0; + v[1].uv[0] = 0; + v[1].uv[1] = 0; + v[2].pos[0] = px1; + v[2].pos[1] = py1; + v[2].uv[0] = 0; + v[2].uv[1] = 0; + v[3].pos[0] = px0; + v[3].pos[1] = py1; + v[3].uv[0] = 0; + v[3].uv[1] = 0; - v[0].col[0] = tr; v[0].col[1] = tg; v[0].col[2] = tb; v[0].col[3] = ta; - v[1].col[0] = tr; v[1].col[1] = tg; v[1].col[2] = tb; v[1].col[3] = ta; - v[2].col[0] = br; v[2].col[1] = bg; v[2].col[2] = bb_; v[2].col[3] = ba; - v[3].col[0] = br; v[3].col[1] = bg; v[3].col[2] = bb_; v[3].col[3] = ba; + v[0].col[0] = tr; + v[0].col[1] = tg; + v[0].col[2] = tb; + v[0].col[3] = ta; + v[1].col[0] = tr; + v[1].col[1] = tg; + v[1].col[2] = tb; + v[1].col[3] = ta; + v[2].col[0] = br; + v[2].col[1] = bg; + v[2].col[2] = bb_; + v[2].col[3] = ba; + v[3].col[0] = br; + v[3].col[1] = bg; + v[3].col[2] = bb_; + v[3].col[3] = ba; for (S32 i = 0; i < 4; i++) { - v[i].rect_min[0] = x0; v[i].rect_min[1] = y0; - v[i].rect_max[0] = x1; v[i].rect_max[1] = y1; - v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr; - v[i].corner_radii[2] = cr_br; v[i].corner_radii[3] = cr_bl; + v[i].rect_min[0] = x0; + v[i].rect_min[1] = y0; + v[i].rect_max[0] = x1; + v[i].rect_max[1] = y1; + v[i].corner_radii[0] = cr_tl; + v[i].corner_radii[1] = cr_tr; + v[i].corner_radii[2] = cr_br; + v[i].corner_radii[3] = cr_bl; v[i].border_thickness = 0; - v[i].softness = softness; - v[i].mode = 0; + v[i].softness = softness; + v[i].mode = 0; } U32 *idx = &batch->indices[batch->index_count]; - idx[0] = base; idx[1] = base + 1; idx[2] = base + 2; - idx[3] = base; idx[4] = base + 2; idx[5] = base + 3; + idx[0] = base; + idx[1] = base + 1; + idx[2] = base + 2; + idx[3] = base; + idx[4] = base + 2; + idx[5] = base + 3; batch->vertex_count += 4; batch->index_count += 6; } static void emit_text_glyphs(DrawBatch *batch, Renderer *r, - Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len, - U16 font_size) -{ + Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len, + U16 font_size) { if (text_len == 0 || color.a < 0.1f) return; F32 cr = color.r / 255.f; @@ -1139,7 +1229,7 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r, F32 cb = color.b / 255.f; F32 ca = color.a / 255.f; - F32 scale = (F32)font_size / r->font_atlas_size; + F32 scale = (F32)font_size / r->font_atlas_size; F32 text_h = r->font_line_height * scale; F32 x = floorf(bbox.x + 0.5f); @@ -1159,17 +1249,17 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r, S32 gi = ch - GLYPH_FIRST; if (gi < 0 || gi >= GLYPH_COUNT) continue; - GlyphInfo *g = &r->glyphs[gi]; - F32 gw = g->w * scale; - F32 gh = g->h * scale; + GlyphInfo *g = &r->glyphs[gi]; + F32 gw = g->w * scale; + F32 gh = g->h * scale; emit_quad(batch, - x, y, x + gw, y + gh, - g->u0, g->v0, g->u1, g->v1, - cr, cg, cb, ca, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 1.0f); + x, y, x + gw, y + gh, + g->u0, g->v0, g->u1, g->v1, + cr, cg, cb, ca, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 1.0f); x += g->x_advance * scale; } @@ -1179,8 +1269,7 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r, // Flush helper static void flush_batch(Renderer *r, DrawBatch *batch, U32 buf_idx, - U32 *flush_index_start, VkDescriptorSet tex_set) -{ + U32 *flush_index_start, VkDescriptorSet tex_set) { U32 draw_index_count = batch->index_count - *flush_index_start; if (draw_index_count == 0) return; @@ -1192,7 +1281,7 @@ static void flush_batch(Renderer *r, DrawBatch *batch, U32 buf_idx, vkCmdPushConstants(cb, r->pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(constants), constants); vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, r->pipeline_layout, - 0, 1, &tex_set, 0, NULL); + 0, 1, &tex_set, 0, NULL); VkDeviceSize offset = 0; vkCmdBindVertexBuffers(cb, 0, 1, &r->vertex_buffers[buf_idx], &offset); @@ -1218,23 +1307,23 @@ Renderer *renderer_create(RendererDesc *desc) { r->clear_b = 0.13f; if (r->frame_count > NUM_BACK_BUFFERS) r->frame_count = NUM_BACK_BUFFERS; - if (!create_instance(r)) goto fail; - if (!create_surface(r)) goto fail; - if (!pick_physical_device(r)) goto fail; - if (!find_queue_family(r)) goto fail; - if (!create_device(r)) goto fail; - if (!create_vma(r)) goto fail; - if (!create_command_resources(r)) goto fail; - if (!create_sampler(r)) goto fail; + if (!create_instance(r)) goto fail; + if (!create_surface(r)) goto fail; + if (!pick_physical_device(r)) goto fail; + if (!find_queue_family(r)) goto fail; + if (!create_device(r)) goto fail; + if (!create_vma(r)) goto fail; + if (!create_command_resources(r)) goto fail; + if (!create_sampler(r)) goto fail; if (!create_descriptor_resources(r)) goto fail; - if (!create_swap_chain(r)) goto fail; - if (!create_render_pass(r)) goto fail; - if (!create_framebuffers(r)) goto fail; - if (!create_pipeline(r)) goto fail; - if (!create_ui_buffers(r)) goto fail; + if (!create_swap_chain(r)) goto fail; + if (!create_render_pass(r)) goto fail; + if (!create_framebuffers(r)) goto fail; + if (!create_pipeline(r)) goto fail; + if (!create_ui_buffers(r)) goto fail; init_freetype(r); - if (!create_font_atlas(r, 22.0f)) goto fail; + if (!create_font_atlas(r, 22.0f)) goto fail; return r; @@ -1256,39 +1345,39 @@ Renderer *renderer_create_shared(Renderer *parent, RendererDesc *desc) { if (r->frame_count > NUM_BACK_BUFFERS) r->frame_count = NUM_BACK_BUFFERS; // Share from parent - r->instance = parent->instance; - r->physical_device = parent->physical_device; - r->device = parent->device; - r->graphics_queue = parent->graphics_queue; - r->queue_family = parent->queue_family; - r->allocator = parent->allocator; - r->render_pass = parent->render_pass; - r->pipeline_layout = parent->pipeline_layout; - r->pipeline = parent->pipeline; + r->instance = parent->instance; + r->physical_device = parent->physical_device; + r->device = parent->device; + r->graphics_queue = parent->graphics_queue; + r->queue_family = parent->queue_family; + r->allocator = parent->allocator; + r->render_pass = parent->render_pass; + r->pipeline_layout = parent->pipeline_layout; + r->pipeline = parent->pipeline; r->descriptor_set_layout = parent->descriptor_set_layout; - r->sampler = parent->sampler; - r->font_image = parent->font_image; - r->font_alloc = parent->font_alloc; - r->font_view = parent->font_view; - r->font_descriptor_set = parent->font_descriptor_set; - r->icon_image = parent->icon_image; - r->icon_alloc = parent->icon_alloc; - r->icon_view = parent->icon_view; - r->icon_descriptor_set = parent->icon_descriptor_set; - r->descriptor_pool = parent->descriptor_pool; - r->ft_lib = parent->ft_lib; - r->ft_face = parent->ft_face; + r->sampler = parent->sampler; + r->font_image = parent->font_image; + r->font_alloc = parent->font_alloc; + r->font_view = parent->font_view; + r->font_descriptor_set = parent->font_descriptor_set; + r->icon_image = parent->icon_image; + r->icon_alloc = parent->icon_alloc; + r->icon_view = parent->icon_view; + r->icon_descriptor_set = parent->icon_descriptor_set; + r->descriptor_pool = parent->descriptor_pool; + r->ft_lib = parent->ft_lib; + r->ft_face = parent->ft_face; memcpy(r->glyphs, parent->glyphs, sizeof(r->glyphs)); - r->font_atlas_size = parent->font_atlas_size; - r->font_line_height = parent->font_line_height; + r->font_atlas_size = parent->font_atlas_size; + r->font_line_height = parent->font_line_height; // Own surface, swap chain, command resources, buffers - if (!create_surface(r)) goto fail; + if (!create_surface(r)) goto fail; if (!create_command_resources(r)) goto fail; - if (!create_swap_chain(r)) goto fail; + if (!create_swap_chain(r)) goto fail; // Shared renderers reuse parent render_pass but format must match - if (!create_framebuffers(r)) goto fail; - if (!create_ui_buffers(r)) goto fail; + if (!create_framebuffers(r)) goto fail; + if (!create_ui_buffers(r)) goto fail; r->clear_r = parent->clear_r; r->clear_g = parent->clear_g; @@ -1345,7 +1434,7 @@ void renderer_destroy(Renderer *r) { if (r->render_pass) vkDestroyRenderPass(r->device, r->render_pass, NULL); if (r->ft_face) FT_Done_Face(r->ft_face); - if (r->ft_lib) FT_Done_FreeType(r->ft_lib); + if (r->ft_lib) FT_Done_FreeType(r->ft_lib); if (r->allocator) vmaDestroyAllocator(r->allocator); if (r->device) vkDestroyDevice(r->device, NULL); @@ -1373,8 +1462,8 @@ B32 renderer_begin_frame(Renderer *r) { r->icon_view = r->parent->icon_view; r->icon_descriptor_set = r->parent->icon_descriptor_set; memcpy(r->glyphs, r->parent->glyphs, sizeof(r->glyphs)); - r->font_atlas_size = r->parent->font_atlas_size; - r->font_line_height = r->parent->font_line_height; + r->font_atlas_size = r->parent->font_atlas_size; + r->font_line_height = r->parent->font_line_height; } if (IsIconic(r->hwnd)) { @@ -1386,17 +1475,17 @@ B32 renderer_begin_frame(Renderer *r) { } void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { - U32 frame_idx = r->frame_index % NUM_BACK_BUFFERS; - FrameContext *fc = &r->frames[frame_idx]; + U32 frame_idx = r->frame_index % NUM_BACK_BUFFERS; + FrameContext *fc = &r->frames[frame_idx]; // Wait for this frame's fence vkWaitForFences(r->device, 1, &fc->fence, VK_TRUE, UINT64_MAX); vkResetFences(r->device, 1, &fc->fence); // Acquire next image - U32 image_index; + U32 image_index; VkResult acquire_result = vkAcquireNextImageKHR(r->device, r->swap_chain, UINT64_MAX, - r->image_available_sema[frame_idx], VK_NULL_HANDLE, &image_index); + r->image_available_sema[frame_idx], VK_NULL_HANDLE, &image_index); if (acquire_result == VK_ERROR_OUT_OF_DATE_KHR) { renderer_resize(r, r->width, r->height); @@ -1406,101 +1495,101 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { VkCommandBuffer cb = fc->command_buffer; vkResetCommandBuffer(cb, 0); - VkCommandBufferBeginInfo begin_info = {0}; - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkCommandBufferBeginInfo begin_info = { 0 }; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(cb, &begin_info); - VkClearValue clear_value = {0}; + VkClearValue clear_value = { 0 }; clear_value.color.float32[0] = r->clear_r; clear_value.color.float32[1] = r->clear_g; clear_value.color.float32[2] = r->clear_b; clear_value.color.float32[3] = 1.0f; - VkRenderPassBeginInfo rp_begin = {0}; - rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - rp_begin.renderPass = r->render_pass; - rp_begin.framebuffer = r->framebuffers[image_index]; - rp_begin.renderArea.extent.width = (U32)r->width; + VkRenderPassBeginInfo rp_begin = { 0 }; + rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rp_begin.renderPass = r->render_pass; + rp_begin.framebuffer = r->framebuffers[image_index]; + rp_begin.renderArea.extent.width = (U32)r->width; rp_begin.renderArea.extent.height = (U32)r->height; - rp_begin.clearValueCount = 1; - rp_begin.pClearValues = &clear_value; + rp_begin.clearValueCount = 1; + rp_begin.pClearValues = &clear_value; vkCmdBeginRenderPass(cb, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - VkViewport viewport = {0}; - viewport.width = (float)r->width; - viewport.height = (float)r->height; - viewport.maxDepth = 1.0f; + VkViewport viewport = { 0 }; + viewport.width = (float)r->width; + viewport.height = (float)r->height; + viewport.maxDepth = 1.0f; vkCmdSetViewport(cb, 0, 1, &viewport); - VkRect2D scissor = {0}; - scissor.extent.width = (U32)r->width; + VkRect2D scissor = { 0 }; + scissor.extent.width = (U32)r->width; scissor.extent.height = (U32)r->height; vkCmdSetScissor(cb, 0, 1, &scissor); // Process Clay render commands if (render_commands.length > 0) { - DrawBatch batch = {0}; - batch.vertices = (UIVertex *)r->vb_mapped[frame_idx]; - batch.indices = (U32 *)r->ib_mapped[frame_idx]; + DrawBatch batch = { 0 }; + batch.vertices = (UIVertex *)r->vb_mapped[frame_idx]; + batch.indices = (U32 *)r->ib_mapped[frame_idx]; - S32 bound_texture = 0; // 0 = font, 1 = icon + S32 bound_texture = 0; // 0 = font, 1 = icon U32 flush_index_start = 0; for (S32 i = 0; i < render_commands.length; i++) { Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i); - Clay_BoundingBox bb = cmd->boundingBox; + Clay_BoundingBox bb = cmd->boundingBox; switch (cmd->commandType) { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { Clay_RectangleRenderData *rect = &cmd->renderData.rectangle; - Clay_Color c = rect->backgroundColor; + Clay_Color c = rect->backgroundColor; emit_rect(&batch, - bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, - c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f, - rect->cornerRadius.topLeft, rect->cornerRadius.topRight, - rect->cornerRadius.bottomRight, rect->cornerRadius.bottomLeft, - 0, 1.0f); + bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, + c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f, + rect->cornerRadius.topLeft, rect->cornerRadius.topRight, + rect->cornerRadius.bottomRight, rect->cornerRadius.bottomLeft, + 0, 1.0f); } break; case CLAY_RENDER_COMMAND_TYPE_BORDER: { - Clay_BorderRenderData *border = &cmd->renderData.border; - Clay_Color c = border->color; - F32 cr_norm = c.r / 255.f; - F32 cg_norm = c.g / 255.f; - F32 cb_norm = c.b / 255.f; - F32 ca_norm = c.a / 255.f; + Clay_BorderRenderData *border = &cmd->renderData.border; + Clay_Color c = border->color; + F32 cr_norm = c.r / 255.f; + F32 cg_norm = c.g / 255.f; + F32 cb_norm = c.b / 255.f; + F32 ca_norm = c.a / 255.f; - Clay_CornerRadius cr = border->cornerRadius; - B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0; - B32 uniform = border->width.top == border->width.bottom && - border->width.top == border->width.left && - border->width.top == border->width.right && - border->width.top > 0; + Clay_CornerRadius cr = border->cornerRadius; + B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0; + B32 uniform = border->width.top == border->width.bottom && + border->width.top == border->width.left && + border->width.top == border->width.right && + border->width.top > 0; if (has_radius && uniform) { emit_rect(&batch, - bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, - cr_norm, cg_norm, cb_norm, ca_norm, - cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft, - (F32)border->width.top, 1.0f); + bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, + cr_norm, cg_norm, cb_norm, ca_norm, + cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft, + (F32)border->width.top, 1.0f); } else { if (border->width.top > 0) { emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top, - cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); + cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); } if (border->width.bottom > 0) { emit_rect(&batch, bb.x, bb.y + bb.height - border->width.bottom, bb.x + bb.width, bb.y + bb.height, - cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); + cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); } if (border->width.left > 0) { emit_rect(&batch, bb.x, bb.y, bb.x + border->width.left, bb.y + bb.height, - cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); + cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); } if (border->width.right > 0) { emit_rect(&batch, bb.x + bb.width - border->width.right, bb.y, bb.x + bb.width, bb.y + bb.height, - cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); + cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f); } } } break; @@ -1514,17 +1603,17 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { } Clay_TextRenderData *text = &cmd->renderData.text; emit_text_glyphs(&batch, r, bb, text->textColor, - text->stringContents.chars, text->stringContents.length, - text->fontSize); + text->stringContents.chars, text->stringContents.length, + text->fontSize); } break; case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: { VkDescriptorSet heap = bound_texture == 1 ? r->icon_descriptor_set : r->font_descriptor_set; flush_batch(r, &batch, frame_idx, &flush_index_start, heap); - VkRect2D clip = {0}; - clip.offset.x = (S32)Max(bb.x, 0.f); - clip.offset.y = (S32)Max(bb.y, 0.f); - clip.extent.width = (U32)Min(bb.width, (F32)r->width - clip.offset.x); + VkRect2D clip = { 0 }; + clip.offset.x = (S32)Max(bb.x, 0.f); + clip.offset.y = (S32)Max(bb.y, 0.f); + clip.extent.width = (U32)Min(bb.width, (F32)r->width - clip.offset.x); clip.extent.height = (U32)Min(bb.height, (F32)r->height - clip.offset.y); if (clip.extent.width == 0) clip.extent.width = 1; if (clip.extent.height == 0) clip.extent.height = 1; @@ -1534,8 +1623,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: { VkDescriptorSet heap = bound_texture == 1 ? r->icon_descriptor_set : r->font_descriptor_set; flush_batch(r, &batch, frame_idx, &flush_index_start, heap); - VkRect2D full_scissor = {0}; - full_scissor.extent.width = (U32)r->width; + VkRect2D full_scissor = { 0 }; + full_scissor.extent.width = (U32)r->width; full_scissor.extent.height = (U32)r->height; vkCmdSetScissor(cb, 0, 1, &full_scissor); } break; @@ -1551,47 +1640,47 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { bound_texture = 0; } CustomGradientData *grad = (CustomGradientData *)custom->customData; - Clay_Color tc = grad->top_color; - Clay_Color bc = grad->bottom_color; + Clay_Color tc = grad->top_color; + Clay_Color bc = grad->bottom_color; emit_rect_vgradient(&batch, - bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, - tc.r / 255.f, tc.g / 255.f, tc.b / 255.f, tc.a / 255.f, - bc.r / 255.f, bc.g / 255.f, bc.b / 255.f, bc.a / 255.f, - custom->cornerRadius.topLeft, custom->cornerRadius.topRight, - custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft, - 1.0f); + bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, + tc.r / 255.f, tc.g / 255.f, tc.b / 255.f, tc.a / 255.f, + bc.r / 255.f, bc.g / 255.f, bc.b / 255.f, bc.a / 255.f, + custom->cornerRadius.topLeft, custom->cornerRadius.topRight, + custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft, + 1.0f); } else if (type == CUSTOM_RENDER_ICON) { if (bound_texture != 1 && r->icon_descriptor_set) { flush_batch(r, &batch, frame_idx, &flush_index_start, r->font_descriptor_set); bound_texture = 1; } - CustomIconData *icon = (CustomIconData *)custom->customData; - Clay_Color c = icon->color; - F32 icon_cr = c.r / 255.f, icon_cg = c.g / 255.f; - F32 icon_cb = c.b / 255.f, icon_ca = c.a / 255.f; - UI_IconInfo *info = &g_icons[icon->icon_id]; + CustomIconData *icon = (CustomIconData *)custom->customData; + Clay_Color c = icon->color; + F32 icon_cr = c.r / 255.f, icon_cg = c.g / 255.f; + F32 icon_cb = c.b / 255.f, icon_ca = c.a / 255.f; + UI_IconInfo *info = &g_icons[icon->icon_id]; emit_quad(&batch, - bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, - info->u0, info->v0, info->u1, info->v1, - icon_cr, icon_cg, icon_cb, icon_ca, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 2.0f); + bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, + info->u0, info->v0, info->u1, info->v1, + icon_cr, icon_cg, icon_cb, icon_ca, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 2.0f); } else if (type == CUSTOM_RENDER_ROTATED_ICON) { if (bound_texture != 1 && r->icon_descriptor_set) { flush_batch(r, &batch, frame_idx, &flush_index_start, r->font_descriptor_set); bound_texture = 1; } - CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData; - Clay_Color c = ri->color; - F32 ri_cr = c.r / 255.f, ri_cg = c.g / 255.f; - F32 ri_cb = c.b / 255.f, ri_ca = c.a / 255.f; - UI_IconInfo *info = &g_icons[ri->icon_id]; + CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData; + Clay_Color c = ri->color; + F32 ri_cr = c.r / 255.f, ri_cg = c.g / 255.f; + F32 ri_cb = c.b / 255.f, ri_ca = c.a / 255.f; + UI_IconInfo *info = &g_icons[ri->icon_id]; emit_quad_rotated(&batch, - bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, - info->u0, info->v0, info->u1, info->v1, - ri_cr, ri_cg, ri_cb, ri_ca, - ri->angle_rad); + bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, + info->u0, info->v0, info->u1, info->v1, + ri_cr, ri_cg, ri_cb, ri_ca, + ri->angle_rad); } } } break; @@ -1612,26 +1701,26 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { // Submit VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo submit = {0}; - submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit.waitSemaphoreCount = 1; - submit.pWaitSemaphores = &r->image_available_sema[frame_idx]; - submit.pWaitDstStageMask = &wait_stage; - submit.commandBufferCount = 1; - submit.pCommandBuffers = &cb; - submit.signalSemaphoreCount = 1; - submit.pSignalSemaphores = &r->render_finished_sema[frame_idx]; + VkSubmitInfo submit = { 0 }; + submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit.waitSemaphoreCount = 1; + submit.pWaitSemaphores = &r->image_available_sema[frame_idx]; + submit.pWaitDstStageMask = &wait_stage; + submit.commandBufferCount = 1; + submit.pCommandBuffers = &cb; + submit.signalSemaphoreCount = 1; + submit.pSignalSemaphores = &r->render_finished_sema[frame_idx]; vkQueueSubmit(r->graphics_queue, 1, &submit, fc->fence); // Present - VkPresentInfoKHR present = {0}; - present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + VkPresentInfoKHR present = { 0 }; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.waitSemaphoreCount = 1; - present.pWaitSemaphores = &r->render_finished_sema[frame_idx]; - present.swapchainCount = 1; - present.pSwapchains = &r->swap_chain; - present.pImageIndices = &image_index; - VkResult present_result = vkQueuePresentKHR(r->graphics_queue, &present); + present.pWaitSemaphores = &r->render_finished_sema[frame_idx]; + present.swapchainCount = 1; + present.pSwapchains = &r->swap_chain; + present.pImageIndices = &image_index; + VkResult present_result = vkQueuePresentKHR(r->graphics_queue, &present); if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR) { renderer_resize(r, r->width, r->height); @@ -1642,36 +1731,36 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) { // Create image - VkImageCreateInfo img_ci = {0}; - img_ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - img_ci.imageType = VK_IMAGE_TYPE_2D; - img_ci.format = VK_FORMAT_R8G8B8A8_UNORM; - img_ci.extent.width = (U32)w; - img_ci.extent.height = (U32)h; - img_ci.extent.depth = 1; - img_ci.mipLevels = 1; - img_ci.arrayLayers = 1; - img_ci.samples = VK_SAMPLE_COUNT_1_BIT; - img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; - img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + VkImageCreateInfo img_ci = { 0 }; + img_ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + img_ci.imageType = VK_IMAGE_TYPE_2D; + img_ci.format = VK_FORMAT_R8G8B8A8_UNORM; + img_ci.extent.width = (U32)w; + img_ci.extent.height = (U32)h; + img_ci.extent.depth = 1; + img_ci.mipLevels = 1; + img_ci.arrayLayers = 1; + img_ci.samples = VK_SAMPLE_COUNT_1_BIT; + img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; + img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; + VmaAllocationCreateInfo alloc_ci = { 0 }; + alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; vmaCreateImage(r->allocator, &img_ci, &alloc_ci, &r->icon_image, &r->icon_alloc, NULL); // Staging buffer - VkBufferCreateInfo staging_ci = {0}; - staging_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - staging_ci.size = (VkDeviceSize)w * h * 4; - staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkBufferCreateInfo staging_ci = { 0 }; + staging_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + staging_ci.size = (VkDeviceSize)w * h * 4; + staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo staging_alloc_ci = {0}; - staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; - staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + VmaAllocationCreateInfo staging_alloc_ci = { 0 }; + staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; + staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - VkBuffer staging_buf; - VmaAllocation staging_alloc; + VkBuffer staging_buf; + VmaAllocation staging_alloc; VmaAllocationInfo staging_info; vmaCreateBuffer(r->allocator, &staging_ci, &staging_alloc_ci, &staging_buf, &staging_alloc, &staging_info); memcpy(staging_info.pMappedData, data, (size_t)w * h * 4); @@ -1679,43 +1768,43 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) { VkCommandBuffer cb = begin_one_shot(r); transition_image(cb, r->icon_image, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkBufferImageCopy region = {0}; + VkBufferImageCopy region = { 0 }; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; - region.imageExtent.width = (U32)w; - region.imageExtent.height = (U32)h; - region.imageExtent.depth = 1; + region.imageExtent.width = (U32)w; + region.imageExtent.height = (U32)h; + region.imageExtent.depth = 1; vkCmdCopyBufferToImage(cb, staging_buf, r->icon_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); transition_image(cb, r->icon_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); end_one_shot(r, cb); vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc); // Image view - VkImageViewCreateInfo view_ci = {0}; - view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_ci.image = r->icon_image; - view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_ci.format = VK_FORMAT_R8G8B8A8_UNORM; + VkImageViewCreateInfo view_ci = { 0 }; + view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_ci.image = r->icon_image; + view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_ci.format = VK_FORMAT_R8G8B8A8_UNORM; view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_ci.subresourceRange.levelCount = 1; view_ci.subresourceRange.layerCount = 1; vkCreateImageView(r->device, &view_ci, NULL, &r->icon_view); // Allocate icon descriptor set - VkDescriptorSetAllocateInfo ds_alloc = {0}; - ds_alloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - ds_alloc.descriptorPool = r->descriptor_pool; - ds_alloc.descriptorSetCount = 1; - ds_alloc.pSetLayouts = &r->descriptor_set_layout; + VkDescriptorSetAllocateInfo ds_alloc = { 0 }; + ds_alloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + ds_alloc.descriptorPool = r->descriptor_pool; + ds_alloc.descriptorSetCount = 1; + ds_alloc.pSetLayouts = &r->descriptor_set_layout; vkAllocateDescriptorSets(r->device, &ds_alloc, &r->icon_descriptor_set); update_descriptor_set(r, r->icon_descriptor_set, r->icon_view); @@ -1728,13 +1817,19 @@ void renderer_resize(Renderer *r, S32 width, S32 height) { // Clean up old swap chain resources for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) { - if (r->framebuffers[i]) { vkDestroyFramebuffer(r->device, r->framebuffers[i], NULL); r->framebuffers[i] = VK_NULL_HANDLE; } - if (r->swap_chain_views[i]) { vkDestroyImageView(r->device, r->swap_chain_views[i], NULL); r->swap_chain_views[i] = VK_NULL_HANDLE; } + if (r->framebuffers[i]) { + vkDestroyFramebuffer(r->device, r->framebuffers[i], NULL); + r->framebuffers[i] = VK_NULL_HANDLE; + } + if (r->swap_chain_views[i]) { + vkDestroyImageView(r->device, r->swap_chain_views[i], NULL); + r->swap_chain_views[i] = VK_NULL_HANDLE; + } } VkSwapchainKHR old_swap = r->swap_chain; - r->width = width; - r->height = height; + r->width = width; + r->height = height; create_swap_chain(r); @@ -1755,14 +1850,20 @@ void renderer_set_font_scale(Renderer *r, F32 scale) { F32 target_size = 22.0f * scale; if (fabsf(target_size - r->font_atlas_size) < 0.1f) return; vkDeviceWaitIdle(r->device); - if (r->font_view) { vkDestroyImageView(r->device, r->font_view, NULL); r->font_view = VK_NULL_HANDLE; } - if (r->font_image) { vmaDestroyImage(r->allocator, r->font_image, r->font_alloc); r->font_image = VK_NULL_HANDLE; } + if (r->font_view) { + vkDestroyImageView(r->device, r->font_view, NULL); + r->font_view = VK_NULL_HANDLE; + } + if (r->font_image) { + vmaDestroyImage(r->allocator, r->font_image, r->font_alloc); + r->font_image = VK_NULL_HANDLE; + } create_font_atlas(r, target_size); } void renderer_sync_from_parent(Renderer *r) { if (!r || !r->parent) return; - Renderer *p = r->parent; + Renderer *p = r->parent; r->font_image = p->font_image; r->font_view = p->font_view; r->font_descriptor_set = p->font_descriptor_set; diff --git a/src/ui/ui_core.cpp b/src/ui/ui_core.cpp index 9729161..6d8af88 100644 --- a/src/ui/ui_core.cpp +++ b/src/ui/ui_core.cpp @@ -18,8 +18,8 @@ F32 g_ui_scale = 1.0f; //////////////////////////////// // Theme global -UI_Theme g_theme = {}; -S32 g_theme_id = 0; +UI_Theme g_theme = {}; +S32 g_theme_id = 0; void ui_set_theme(S32 theme_id) { g_theme_id = theme_id; @@ -27,55 +27,55 @@ void ui_set_theme(S32 theme_id) { switch (theme_id) { default: case 0: // Dark - g_theme.bg_dark = Clay_Color{ 26, 26, 26, 255}; - g_theme.bg_medium = Clay_Color{ 36, 36, 36, 255}; - g_theme.bg_light = Clay_Color{ 46, 46, 46, 255}; - g_theme.bg_lighter = Clay_Color{ 54, 54, 54, 255}; - g_theme.border = Clay_Color{ 52, 52, 52, 255}; - g_theme.text = Clay_Color{220, 220, 220, 255}; - g_theme.text_dim = Clay_Color{105, 105, 105, 255}; - g_theme.accent = Clay_Color{ 87, 138, 176, 255}; - g_theme.accent_hover = Clay_Color{102, 153, 191, 255}; - g_theme.button_text = Clay_Color{224, 224, 224, 255}; - g_theme.disabled_bg = Clay_Color{ 44, 44, 44, 255}; - g_theme.disabled_text = Clay_Color{ 90, 90, 90, 255}; - g_theme.header_bg = Clay_Color{ 46, 46, 46, 255}; - g_theme.title_bar = Clay_Color{ 22, 22, 22, 255}; - g_theme.scrollbar_bg = Clay_Color{ 22, 22, 22, 255}; - g_theme.scrollbar_grab = Clay_Color{ 58, 58, 58, 255}; - g_theme.shadow = Clay_Color{ 0, 0, 0, 30}; - g_theme.tab_active_top = Clay_Color{ 70, 120, 160, 255}; - g_theme.tab_active_bottom= Clay_Color{ 30, 55, 80, 255}; - g_theme.tab_inactive = Clay_Color{ 40, 40, 40, 255}; - g_theme.tab_inactive_hover= Clay_Color{ 50, 50, 50, 255}; - g_theme.tab_text = Clay_Color{240, 240, 240, 255}; - g_theme.corner_radius = 4.0f; + g_theme.bg_dark = Clay_Color{ 26, 26, 26, 255 }; + g_theme.bg_medium = Clay_Color{ 36, 36, 36, 255 }; + g_theme.bg_light = Clay_Color{ 46, 46, 46, 255 }; + g_theme.bg_lighter = Clay_Color{ 54, 54, 54, 255 }; + g_theme.border = Clay_Color{ 52, 52, 52, 255 }; + g_theme.text = Clay_Color{ 220, 220, 220, 255 }; + g_theme.text_dim = Clay_Color{ 105, 105, 105, 255 }; + g_theme.accent = Clay_Color{ 87, 138, 176, 255 }; + g_theme.accent_hover = Clay_Color{ 102, 153, 191, 255 }; + g_theme.button_text = Clay_Color{ 224, 224, 224, 255 }; + g_theme.disabled_bg = Clay_Color{ 44, 44, 44, 255 }; + g_theme.disabled_text = Clay_Color{ 90, 90, 90, 255 }; + g_theme.header_bg = Clay_Color{ 46, 46, 46, 255 }; + g_theme.title_bar = Clay_Color{ 22, 22, 22, 255 }; + g_theme.scrollbar_bg = Clay_Color{ 22, 22, 22, 255 }; + g_theme.scrollbar_grab = Clay_Color{ 58, 58, 58, 255 }; + g_theme.shadow = Clay_Color{ 0, 0, 0, 30 }; + g_theme.tab_active_top = Clay_Color{ 70, 120, 160, 255 }; + g_theme.tab_active_bottom = Clay_Color{ 30, 55, 80, 255 }; + g_theme.tab_inactive = Clay_Color{ 40, 40, 40, 255 }; + g_theme.tab_inactive_hover = Clay_Color{ 50, 50, 50, 255 }; + g_theme.tab_text = Clay_Color{ 240, 240, 240, 255 }; + g_theme.corner_radius = 4.0f; break; case 1: // Light - g_theme.bg_dark = Clay_Color{195, 193, 190, 255}; - g_theme.bg_medium = Clay_Color{212, 210, 207, 255}; - g_theme.bg_light = Clay_Color{225, 223, 220, 255}; - g_theme.bg_lighter = Clay_Color{232, 230, 227, 255}; - g_theme.border = Clay_Color{178, 176, 173, 255}; - g_theme.text = Clay_Color{ 35, 33, 30, 255}; - g_theme.text_dim = Clay_Color{115, 113, 108, 255}; - g_theme.accent = Clay_Color{ 50, 110, 170, 255}; - g_theme.accent_hover = Clay_Color{ 65, 125, 185, 255}; - g_theme.button_text = Clay_Color{255, 255, 255, 255}; - g_theme.disabled_bg = Clay_Color{185, 183, 180, 255}; - g_theme.disabled_text = Clay_Color{145, 143, 140, 255}; - g_theme.header_bg = Clay_Color{202, 200, 197, 255}; - g_theme.title_bar = Clay_Color{202, 200, 197, 255}; - g_theme.scrollbar_bg = Clay_Color{202, 200, 197, 255}; - g_theme.scrollbar_grab = Clay_Color{168, 166, 163, 255}; - g_theme.shadow = Clay_Color{ 0, 0, 0, 18}; - g_theme.tab_active_top = Clay_Color{ 70, 130, 180, 255}; - g_theme.tab_active_bottom= Clay_Color{ 50, 100, 150, 255}; - g_theme.tab_inactive = Clay_Color{205, 203, 200, 255}; - g_theme.tab_inactive_hover= Clay_Color{195, 193, 190, 255}; - g_theme.tab_text = Clay_Color{255, 255, 255, 255}; - g_theme.corner_radius = 4.0f; + g_theme.bg_dark = Clay_Color{ 195, 193, 190, 255 }; + g_theme.bg_medium = Clay_Color{ 212, 210, 207, 255 }; + g_theme.bg_light = Clay_Color{ 225, 223, 220, 255 }; + g_theme.bg_lighter = Clay_Color{ 232, 230, 227, 255 }; + g_theme.border = Clay_Color{ 178, 176, 173, 255 }; + g_theme.text = Clay_Color{ 35, 33, 30, 255 }; + g_theme.text_dim = Clay_Color{ 115, 113, 108, 255 }; + g_theme.accent = Clay_Color{ 50, 110, 170, 255 }; + g_theme.accent_hover = Clay_Color{ 65, 125, 185, 255 }; + g_theme.button_text = Clay_Color{ 255, 255, 255, 255 }; + g_theme.disabled_bg = Clay_Color{ 185, 183, 180, 255 }; + g_theme.disabled_text = Clay_Color{ 145, 143, 140, 255 }; + g_theme.header_bg = Clay_Color{ 202, 200, 197, 255 }; + g_theme.title_bar = Clay_Color{ 202, 200, 197, 255 }; + g_theme.scrollbar_bg = Clay_Color{ 202, 200, 197, 255 }; + g_theme.scrollbar_grab = Clay_Color{ 168, 166, 163, 255 }; + g_theme.shadow = Clay_Color{ 0, 0, 0, 18 }; + g_theme.tab_active_top = Clay_Color{ 70, 130, 180, 255 }; + g_theme.tab_active_bottom = Clay_Color{ 50, 100, 150, 255 }; + g_theme.tab_inactive = Clay_Color{ 205, 203, 200, 255 }; + g_theme.tab_inactive_hover = Clay_Color{ 195, 193, 190, 255 }; + g_theme.tab_text = Clay_Color{ 255, 255, 255, 255 }; + g_theme.corner_radius = 4.0f; break; } } @@ -87,7 +87,7 @@ S32 g_accent_id = 0; void ui_set_accent(S32 accent_id) { g_accent_id = accent_id; - B32 dark = (g_theme_id == 0); + B32 dark = (g_theme_id == 0); // Each palette: accent, accent_hover, tab_top, tab_bottom, button_text, tab_text struct AccentColors { @@ -97,44 +97,44 @@ void ui_set_accent(S32 accent_id) { // Dark-mode palettes static const AccentColors dark_palettes[] = { // 0: Blue - { {87,138,176,255}, {102,153,191,255}, {70,120,160,255}, {30,55,80,255}, {224,224,224,255}, {240,240,240,255} }, + { { 87, 138, 176, 255 }, { 102, 153, 191, 255 }, { 70, 120, 160, 255 }, { 30, 55, 80, 255 }, { 224, 224, 224, 255 }, { 240, 240, 240, 255 } }, // 1: Turquoise - { {60,160,155,255}, {75,175,170,255}, {50,140,135,255}, {25,70,68,255}, {224,224,224,255}, {240,240,240,255} }, + { { 60, 160, 155, 255 }, { 75, 175, 170, 255 }, { 50, 140, 135, 255 }, { 25, 70, 68, 255 }, { 224, 224, 224, 255 }, { 240, 240, 240, 255 } }, // 2: Orange - { {200,130,50,255}, {215,145,65,255}, {190,120,40,255}, {100,60,20,255}, {240,240,240,255}, {240,240,240,255} }, + { { 200, 130, 50, 255 }, { 215, 145, 65, 255 }, { 190, 120, 40, 255 }, { 100, 60, 20, 255 }, { 240, 240, 240, 255 }, { 240, 240, 240, 255 } }, // 3: Purple - { {130,100,180,255}, {145,115,195,255}, {120,90,170,255}, {60,45,85,255}, {224,224,224,255}, {240,240,240,255} }, + { { 130, 100, 180, 255 }, { 145, 115, 195, 255 }, { 120, 90, 170, 255 }, { 60, 45, 85, 255 }, { 224, 224, 224, 255 }, { 240, 240, 240, 255 } }, // 4: Pink - { {185,95,140,255}, {200,110,155,255}, {175,85,130,255}, {88,42,65,255}, {240,240,240,255}, {240,240,240,255} }, + { { 185, 95, 140, 255 }, { 200, 110, 155, 255 }, { 175, 85, 130, 255 }, { 88, 42, 65, 255 }, { 240, 240, 240, 255 }, { 240, 240, 240, 255 } }, // 5: Red - { {190,75,75,255}, {205,90,90,255}, {180,65,65,255}, {90,32,32,255}, {240,240,240,255}, {240,240,240,255} }, + { { 190, 75, 75, 255 }, { 205, 90, 90, 255 }, { 180, 65, 65, 255 }, { 90, 32, 32, 255 }, { 240, 240, 240, 255 }, { 240, 240, 240, 255 } }, // 6: Green - { {80,155,80,255}, {95,170,95,255}, {70,140,70,255}, {35,70,35,255}, {240,240,240,255}, {240,240,240,255} }, + { { 80, 155, 80, 255 }, { 95, 170, 95, 255 }, { 70, 140, 70, 255 }, { 35, 70, 35, 255 }, { 240, 240, 240, 255 }, { 240, 240, 240, 255 } }, }; // Light-mode palettes (slightly more saturated for contrast on light bg) static const AccentColors light_palettes[] = { // 0: Blue - { {50,110,170,255}, {65,125,185,255}, {70,130,180,255}, {50,100,150,255}, {255,255,255,255}, {255,255,255,255} }, + { { 50, 110, 170, 255 }, { 65, 125, 185, 255 }, { 70, 130, 180, 255 }, { 50, 100, 150, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 1: Turquoise - { {30,140,135,255}, {45,155,150,255}, {40,150,145,255}, {25,115,110,255}, {255,255,255,255}, {255,255,255,255} }, + { { 30, 140, 135, 255 }, { 45, 155, 150, 255 }, { 40, 150, 145, 255 }, { 25, 115, 110, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 2: Orange - { {190,115,25,255}, {205,130,40,255}, {195,120,30,255}, {155,90,15,255}, {255,255,255,255}, {255,255,255,255} }, + { { 190, 115, 25, 255 }, { 205, 130, 40, 255 }, { 195, 120, 30, 255 }, { 155, 90, 15, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 3: Purple - { {110,75,170,255}, {125,90,185,255}, {115,80,175,255}, {85,55,140,255}, {255,255,255,255}, {255,255,255,255} }, + { { 110, 75, 170, 255 }, { 125, 90, 185, 255 }, { 115, 80, 175, 255 }, { 85, 55, 140, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 4: Pink - { {175,70,125,255}, {190,85,140,255}, {180,75,130,255}, {140,50,100,255}, {255,255,255,255}, {255,255,255,255} }, + { { 175, 70, 125, 255 }, { 190, 85, 140, 255 }, { 180, 75, 130, 255 }, { 140, 50, 100, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 5: Red - { {185,55,55,255}, {200,70,70,255}, {190,60,60,255}, {150,40,40,255}, {255,255,255,255}, {255,255,255,255} }, + { { 185, 55, 55, 255 }, { 200, 70, 70, 255 }, { 190, 60, 60, 255 }, { 150, 40, 40, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, // 6: Green - { {55,140,55,255}, {70,155,70,255}, {60,145,60,255}, {40,110,40,255}, {255,255,255,255}, {255,255,255,255} }, + { { 55, 140, 55, 255 }, { 70, 155, 70, 255 }, { 60, 145, 60, 255 }, { 40, 110, 40, 255 }, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } }, }; S32 idx = accent_id; if (idx < 0) idx = 0; if (idx > 6) idx = 6; - const AccentColors &c = dark ? dark_palettes[idx] : light_palettes[idx]; + const AccentColors &c = dark ? dark_palettes[idx] : light_palettes[idx]; g_theme.accent = c.accent; g_theme.accent_hover = c.accent_hover; g_theme.tab_active_top = c.tab_top; @@ -148,7 +148,7 @@ void ui_set_accent(S32 accent_id) { static void clay_error_handler(Clay_ErrorData error) { char buf[512]; - S32 len = error.errorText.length < 511 ? error.errorText.length : 511; + S32 len = error.errorText.length < 511 ? error.errorText.length : 511; memcpy(buf, error.errorText.chars, len); buf[len] = '\0'; fprintf(stderr, "[Clay Error] %s\n", buf); @@ -163,10 +163,10 @@ static UI_Context *g_measure_ctx = nullptr; static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *user_data) { UI_Context *ctx = (UI_Context *)user_data; if (!ctx || !ctx->measure_text_fn || text.length == 0) { - return Clay_Dimensions{0, (F32)config->fontSize}; + return Clay_Dimensions{ 0, (F32)config->fontSize }; } Vec2F32 result = ctx->measure_text_fn(text.chars, text.length, (F32)config->fontSize, ctx->measure_text_user_data); - return Clay_Dimensions{result.x, result.y}; + return Clay_Dimensions{ result.x, result.y }; } //////////////////////////////// @@ -175,17 +175,17 @@ static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElement UI_Context *ui_create(F32 viewport_w, F32 viewport_h) { UI_Context *ctx = (UI_Context *)calloc(1, sizeof(UI_Context)); - U32 min_memory = Clay_MinMemorySize(); - ctx->clay_memory = malloc(min_memory); + U32 min_memory = Clay_MinMemorySize(); + ctx->clay_memory = malloc(min_memory); Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(min_memory, ctx->clay_memory); - Clay_ErrorHandler err_handler = {}; + Clay_ErrorHandler err_handler = {}; err_handler.errorHandlerFunction = clay_error_handler; - err_handler.userData = ctx; + err_handler.userData = ctx; ctx->clay_ctx = Clay_Initialize(clay_arena, - Clay_Dimensions{viewport_w, viewport_h}, - err_handler); + Clay_Dimensions{ viewport_w, viewport_h }, + err_handler); Clay_SetMeasureTextFunction(clay_measure_text, ctx); @@ -200,13 +200,12 @@ void ui_destroy(UI_Context *ctx) { void ui_begin_frame(UI_Context *ctx, F32 viewport_w, F32 viewport_h, Vec2F32 mouse_pos, B32 mouse_down, - Vec2F32 scroll_delta, F32 dt) -{ + Vec2F32 scroll_delta, F32 dt) { g_measure_ctx = ctx; Clay_SetCurrentContext(ctx->clay_ctx); - Clay_SetLayoutDimensions(Clay_Dimensions{viewport_w, viewport_h}); - Clay_SetPointerState(Clay_Vector2{mouse_pos.x, mouse_pos.y}, mouse_down != 0); - Clay_UpdateScrollContainers(false, Clay_Vector2{scroll_delta.x, scroll_delta.y}, dt); + Clay_SetLayoutDimensions(Clay_Dimensions{ viewport_w, viewport_h }); + Clay_SetPointerState(Clay_Vector2{ mouse_pos.x, mouse_pos.y }, mouse_down != 0); + Clay_UpdateScrollContainers(false, Clay_Vector2{ scroll_delta.x, scroll_delta.y }, dt); Clay_BeginLayout(); } @@ -216,7 +215,7 @@ Clay_RenderCommandArray ui_end_frame(UI_Context *ctx) { } void ui_set_measure_text_fn(UI_Context *ctx, UI_MeasureTextFn fn, void *user_data) { - ctx->measure_text_fn = fn; + ctx->measure_text_fn = fn; ctx->measure_text_user_data = user_data; } diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 15d2b48..0b2df13 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -15,8 +15,8 @@ typedef Vec2F32 (*UI_MeasureTextFn)(const char *text, S32 length, F32 font_size, // UI Context struct UI_Context { - Clay_Context *clay_ctx; - void *clay_memory; + Clay_Context *clay_ctx; + void *clay_memory; // Text measurement UI_MeasureTextFn measure_text_fn; @@ -65,28 +65,28 @@ struct UI_Theme { Clay_Color title_bar; Clay_Color scrollbar_bg; Clay_Color scrollbar_grab; - Clay_Color shadow; // Semi-transparent black for drop shadows + Clay_Color shadow; // Semi-transparent black for drop shadows // Tab bar colors Clay_Color tab_active_top; Clay_Color tab_active_bottom; Clay_Color tab_inactive; Clay_Color tab_inactive_hover; - Clay_Color tab_text; // Always light — readable on colored tab gradient + Clay_Color tab_text; // Always light — readable on colored tab gradient // Corner radius (unscaled pixels, applied via uis()) F32 corner_radius; }; extern UI_Theme g_theme; -extern S32 g_theme_id; +extern S32 g_theme_id; // Set theme by index: 0 = Dark, 1 = Light void ui_set_theme(S32 theme_id); // Accent color palette: 0=Blue, 1=Turquoise, 2=Orange, 3=Purple, 4=Pink, 5=Red, 6=Green extern S32 g_accent_id; -void ui_set_accent(S32 accent_id); +void ui_set_accent(S32 accent_id); //////////////////////////////// // UI scale (Cmd+/Cmd- zoom) @@ -105,37 +105,37 @@ static inline U16 uifs(F32 x) { return (U16)(x * g_ui_scale + 0.5f); } //////////////////////////////// // Tab styling -#define TAB_ACTIVE_TOP g_theme.tab_active_top -#define TAB_ACTIVE_BOTTOM g_theme.tab_active_bottom -#define TAB_INACTIVE_BG g_theme.tab_inactive -#define TAB_INACTIVE_HOVER g_theme.tab_inactive_hover -#define TAB_HEIGHT uis(26) -#define TAB_CORNER_RADIUS CORNER_RADIUS -#define TAB_PADDING_H uip(10) +#define TAB_ACTIVE_TOP g_theme.tab_active_top +#define TAB_ACTIVE_BOTTOM g_theme.tab_active_bottom +#define TAB_INACTIVE_BG g_theme.tab_inactive +#define TAB_INACTIVE_HOVER g_theme.tab_inactive_hover +#define TAB_HEIGHT uis(26) +#define TAB_CORNER_RADIUS CORNER_RADIUS +#define TAB_PADDING_H uip(10) //////////////////////////////// // Custom render types (for gradient rects via CLAY_RENDER_COMMAND_TYPE_CUSTOM) enum CustomRenderType { - CUSTOM_RENDER_VGRADIENT = 1, - CUSTOM_RENDER_ICON = 2, + CUSTOM_RENDER_VGRADIENT = 1, + CUSTOM_RENDER_ICON = 2, CUSTOM_RENDER_ROTATED_ICON = 3, }; struct CustomGradientData { CustomRenderType type; - Clay_Color top_color; - Clay_Color bottom_color; + Clay_Color top_color; + Clay_Color bottom_color; }; struct CustomIconData { - CustomRenderType type; // CUSTOM_RENDER_ICON + CustomRenderType type; // CUSTOM_RENDER_ICON S32 icon_id; Clay_Color color; }; struct CustomRotatedIconData { - CustomRenderType type; // CUSTOM_RENDER_ROTATED_ICON + CustomRenderType type; // CUSTOM_RENDER_ROTATED_ICON S32 icon_id; Clay_Color color; F32 angle_rad; @@ -144,23 +144,23 @@ struct CustomRotatedIconData { //////////////////////////////// // Font sizes -#define FONT_SIZE_NORMAL uifs(15) -#define FONT_SIZE_SMALL uifs(12) -#define FONT_SIZE_TAB uifs(13) +#define FONT_SIZE_NORMAL uifs(15) +#define FONT_SIZE_SMALL uifs(12) +#define FONT_SIZE_TAB uifs(13) //////////////////////////////// // Widget sizing -#define WIDGET_BUTTON_HEIGHT uis(30) -#define WIDGET_CHECKBOX_HEIGHT uis(28) -#define WIDGET_CHECKBOX_SIZE uis(18) -#define WIDGET_RADIO_OUTER uis(16) -#define WIDGET_RADIO_INNER uis(8) -#define WIDGET_INPUT_HEIGHT uis(30) -#define WIDGET_DROPDOWN_HEIGHT uis(30) -#define WIDGET_DROPDOWN_ITEM_H uis(28) -#define WIDGET_KNOB_SIZE uis(48) -#define WIDGET_KNOB_LABEL_GAP uip(4) +#define WIDGET_BUTTON_HEIGHT uis(30) +#define WIDGET_CHECKBOX_HEIGHT uis(28) +#define WIDGET_CHECKBOX_SIZE uis(18) +#define WIDGET_RADIO_OUTER uis(16) +#define WIDGET_RADIO_INNER uis(8) +#define WIDGET_INPUT_HEIGHT uis(30) +#define WIDGET_DROPDOWN_HEIGHT uis(30) +#define WIDGET_DROPDOWN_ITEM_H uis(28) +#define WIDGET_KNOB_SIZE uis(48) +#define WIDGET_KNOB_LABEL_GAP uip(4) #define WIDGET_SLIDER_H_WIDTH uis(160) #define WIDGET_SLIDER_H_TRACK_H uis(6) @@ -181,11 +181,11 @@ struct CustomRotatedIconData { //////////////////////////////// // Corner radius (from theme) -#define CORNER_RADIUS uis(g_theme.corner_radius) +#define CORNER_RADIUS uis(g_theme.corner_radius) //////////////////////////////// // Panel sizing -#define PANEL_BROWSER_WIDTH uis(200) -#define PANEL_RIGHT_COL_WIDTH uis(250) -#define PANEL_LOG_HEIGHT uis(180) +#define PANEL_BROWSER_WIDTH uis(200) +#define PANEL_RIGHT_COL_WIDTH uis(250) +#define PANEL_LOG_HEIGHT uis(180) diff --git a/src/ui/ui_icons.cpp b/src/ui/ui_icons.cpp index c1641c9..48b2922 100644 --- a/src/ui/ui_icons.cpp +++ b/src/ui/ui_icons.cpp @@ -163,16 +163,16 @@ U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) { if (bmp.isNull()) continue; // Copy BGRA premultiplied → RGBA straight (un-premultiply) - U8 *src = bmp.data(); - S32 bmp_w = bmp.width(); - S32 bmp_h = bmp.height(); + U8 *src = bmp.data(); + S32 bmp_w = bmp.width(); + S32 bmp_h = bmp.height(); S32 stride = bmp.stride(); for (S32 y = 0; y < bmp_h && y < atlas_h; y++) { for (S32 x = 0; x < bmp_w && (pen_x + x) < atlas_w; x++) { - U8 *s = &src[y * stride + x * 4]; + U8 *s = &src[y * stride + x * 4]; S32 dst_idx = (y * atlas_w + pen_x + x) * 4; - U8 b = s[0], g = s[1], r = s[2], a = s[3]; + U8 b = s[0], g = s[1], r = s[2], a = s[3]; if (a > 0 && a < 255) { r = (U8)((r * 255) / a); g = (U8)((g * 255) / a); @@ -190,8 +190,8 @@ U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) { g_icons[i].v0 = 0.0f; g_icons[i].u1 = (F32)(pen_x + bmp_w) / (F32)atlas_w; g_icons[i].v1 = (F32)bmp_h / (F32)atlas_h; - g_icons[i].w = (F32)bmp_w; - g_icons[i].h = (F32)bmp_h; + g_icons[i].w = (F32)bmp_w; + g_icons[i].h = (F32)bmp_h; pen_x += icon_size; } diff --git a/src/ui/ui_icons.h b/src/ui/ui_icons.h index 0bf8973..b19ae50 100644 --- a/src/ui/ui_icons.h +++ b/src/ui/ui_icons.h @@ -20,8 +20,8 @@ enum UI_IconID { }; struct UI_IconInfo { - F32 u0, v0, u1, v1; // UV coordinates in icon atlas - F32 w, h; // pixel dimensions at rasterized size + F32 u0, v0, u1, v1; // UV coordinates in icon atlas + F32 w, h; // pixel dimensions at rasterized size }; extern UI_IconInfo g_icons[UI_ICON_COUNT]; diff --git a/src/ui/ui_piano.cpp b/src/ui/ui_piano.cpp index 7354348..2ab0957 100644 --- a/src/ui/ui_piano.cpp +++ b/src/ui/ui_piano.cpp @@ -14,26 +14,25 @@ Clay_Color piano_velocity_color(S32 velocity) { F32 r, g, b; if (t < 0.5f) { F32 s = t * 2.0f; - r = 40.0f + s * (76.0f - 40.0f); - g = 120.0f + s * (175.0f - 120.0f); - b = 220.0f + s * (80.0f - 220.0f); + r = 40.0f + s * (76.0f - 40.0f); + g = 120.0f + s * (175.0f - 120.0f); + b = 220.0f + s * (80.0f - 220.0f); } else { F32 s = (t - 0.5f) * 2.0f; - r = 76.0f + s * (220.0f - 76.0f); - g = 175.0f + s * (50.0f - 175.0f); - b = 80.0f + s * (40.0f - 80.0f); + r = 76.0f + s * (220.0f - 76.0f); + g = 175.0f + s * (50.0f - 175.0f); + b = 80.0f + s * (40.0f - 80.0f); } - return Clay_Color{r, g, b, 255}; + return Clay_Color{ r, g, b, 255 }; } void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) { Clay_ElementId piano_id = CLAY_ID("PianoContainer"); CLAY(piano_id, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Compute black key size proportional to white keys F32 black_key_h = avail_h * PIANO_BLACK_H_PCT; if (black_key_h < uis(20)) black_key_h = uis(20); @@ -46,24 +45,22 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) { if (piano_is_black_key(note)) continue; - B32 midi_held = midi_is_note_held(midi, note); - B32 mouse_held = state->mouse_note == note; + B32 midi_held = midi_is_note_held(midi, note); + B32 mouse_held = state->mouse_note == note; Clay_Color bg; if (midi_held) { bg = piano_velocity_color(midi_get_note_velocity(midi, note)); } else if (mouse_held) { bg = g_theme.accent; } else { - bg = Clay_Color{240, 240, 240, 255}; + bg = Clay_Color{ 240, 240, 240, 255 }; } CLAY(CLAY_IDI("PKey", note), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - }, - .backgroundColor = bg, - .border = { .color = {190, 190, 190, 255}, .width = { .right = 1 } }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + }, + .backgroundColor = bg, .border = { .color = { 190, 190, 190, 255 }, .width = { .right = 1 } }, ) {} } // Black keys (floating, attached to left white key) @@ -71,36 +68,33 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) if (!piano_is_black_key(note)) continue; Clay_ElementId parent_wkey = CLAY_IDI("PKey", note - 1); - B32 midi_held = midi_is_note_held(midi, note); - B32 mouse_held = state->mouse_note == note; - Clay_Color bg; + B32 midi_held = midi_is_note_held(midi, note); + B32 mouse_held = state->mouse_note == note; + Clay_Color bg; if (midi_held) { bg = piano_velocity_color(midi_get_note_velocity(midi, note)); } else if (mouse_held) { bg = g_theme.accent; } else { - bg = Clay_Color{25, 25, 30, 255}; + bg = Clay_Color{ 25, 25, 30, 255 }; } CLAY(CLAY_IDI("PKey", note), - .layout = { - .sizing = { - .width = CLAY_SIZING_FIXED(black_key_w), - .height = CLAY_SIZING_FIXED(black_key_h), - }, - }, - .backgroundColor = bg, - .cornerRadius = { .topLeft = 0, .topRight = 0, .bottomLeft = uis(2), .bottomRight = uis(2) }, - .floating = { - .parentId = parent_wkey.id, - .zIndex = 100, - .attachPoints = { - .element = CLAY_ATTACH_POINT_CENTER_TOP, - .parent = CLAY_ATTACH_POINT_RIGHT_TOP, - }, - .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, - }, - ) {} + .layout = { + .sizing = { + .width = CLAY_SIZING_FIXED(black_key_w), + .height = CLAY_SIZING_FIXED(black_key_h), + }, + }, + .backgroundColor = bg, .cornerRadius = { .topLeft = 0, .topRight = 0, .bottomLeft = uis(2), .bottomRight = uis(2) }, .floating = { + .parentId = parent_wkey.id, + .zIndex = 100, + .attachPoints = { + .element = CLAY_ATTACH_POINT_CENTER_TOP, + .parent = CLAY_ATTACH_POINT_RIGHT_TOP, + }, + .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, + }, ) {} } } } diff --git a/src/ui/ui_piano.h b/src/ui/ui_piano.h index d883365..ea0aa1a 100644 --- a/src/ui/ui_piano.h +++ b/src/ui/ui_piano.h @@ -1,12 +1,12 @@ #pragma once -#include "ui/ui_core.h" #include "midi/midi.h" +#include "ui/ui_core.h" -#define PIANO_FIRST_NOTE 21 // A0 -#define PIANO_LAST_NOTE 108 // C8 +#define PIANO_FIRST_NOTE 21 // A0 +#define PIANO_LAST_NOTE 108 // C8 struct UI_PianoState { - S32 mouse_note; // MIDI note held by mouse click (-1 = none) + S32 mouse_note; // MIDI note held by mouse click (-1 = none) }; B32 piano_is_black_key(S32 note); diff --git a/src/ui/ui_popups.cpp b/src/ui/ui_popups.cpp index 4f938d8..8ddddca 100644 --- a/src/ui/ui_popups.cpp +++ b/src/ui/ui_popups.cpp @@ -23,7 +23,10 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer // Find free slot PopupWindow *popup = nullptr; for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) { - if (!g_popups[i].alive) { popup = &g_popups[i]; break; } + if (!g_popups[i].alive) { + popup = &g_popups[i]; + break; + } } if (!popup) return nullptr; @@ -31,24 +34,24 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer // Create native popup window PlatformWindowDesc desc = {}; - desc.title = title; - desc.width = width; - desc.height = height; - desc.style = style; - desc.parent = parent_window; - desc.independent = independent; - popup->platform_window = platform_create_window(&desc); + desc.title = title; + desc.width = width; + desc.height = height; + desc.style = style; + desc.parent = parent_window; + desc.independent = independent; + popup->platform_window = platform_create_window(&desc); if (!popup->platform_window) return nullptr; // Create shared renderer S32 pw, ph; platform_get_size(popup->platform_window, &pw, &ph); - RendererDesc rdesc = {}; + RendererDesc rdesc = {}; rdesc.window_handle = platform_get_native_handle(popup->platform_window); rdesc.width = pw; rdesc.height = ph; - popup->renderer = renderer_create_shared(parent_renderer, &rdesc); + popup->renderer = renderer_create_shared(parent_renderer, &rdesc); if (!popup->renderer) { platform_destroy_window(popup->platform_window); return nullptr; @@ -58,16 +61,16 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer popup->ui_ctx = ui_create((F32)pw, (F32)ph); ui_set_measure_text_fn(popup->ui_ctx, renderer_measure_text, popup->renderer); - popup->alive = 1; - popup->open_flag = open_flag; - popup->content_fn = content_fn; + popup->alive = 1; + popup->open_flag = open_flag; + popup->content_fn = content_fn; popup->content_user_data = user_data; - popup->width = width; - popup->height = height; - popup->last_w = pw; - popup->last_h = ph; - popup->title = title; - popup->wstate = {}; + popup->width = width; + popup->height = height; + popup->last_w = pw; + popup->last_h = ph; + popup->title = title; + popup->wstate = {}; platform_set_frame_callback(popup->platform_window, popup_frame_callback, popup); @@ -111,7 +114,7 @@ void popup_do_frame(PopupWindow *popup, F32 dt) { if (input.ctrl_held) { if (input.keys[k] == PKEY_EQUAL) g_ui_scale *= 1.1f; if (input.keys[k] == PKEY_MINUS) g_ui_scale /= 1.1f; - if (input.keys[k] == PKEY_0) g_ui_scale = 1.0f; + if (input.keys[k] == PKEY_0) g_ui_scale = 1.0f; } } g_ui_scale = Clamp(0.5f, g_ui_scale, 3.0f); @@ -121,7 +124,7 @@ void popup_do_frame(PopupWindow *popup, F32 dt) { // Swap widget state UI_WidgetState saved_wstate = g_wstate; - g_wstate = popup->wstate; + g_wstate = popup->wstate; ui_widgets_begin_frame(input); @@ -131,14 +134,13 @@ void popup_do_frame(PopupWindow *popup, F32 dt) { // Background fill CLAY(CLAY_ID("PopupBg"), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, - .padding = { uip(12), uip(12), uip(10), uip(10) }, - .childGap = uip(8), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = g_theme.bg_medium, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() }, + .padding = { uip(12), uip(12), uip(10), uip(10) }, + .childGap = uip(8), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = g_theme.bg_medium, ) { if (popup->content_fn) { popup->content_fn(popup->content_user_data); } @@ -148,7 +150,7 @@ void popup_do_frame(PopupWindow *popup, F32 dt) { // Save widget state back popup->wstate = g_wstate; - g_wstate = saved_wstate; + g_wstate = saved_wstate; // Render renderer_end_frame(popup->renderer, render_commands); diff --git a/src/ui/ui_popups.h b/src/ui/ui_popups.h index b4c8396..25aad8f 100644 --- a/src/ui/ui_popups.h +++ b/src/ui/ui_popups.h @@ -1,31 +1,31 @@ #pragma once -#include "ui/ui_core.h" -#include "ui/ui_widgets.h" #include "platform/platform.h" #include "renderer/renderer.h" +#include "ui/ui_core.h" +#include "ui/ui_widgets.h" #define MAX_POPUP_WINDOWS 4 struct PopupWindow { - B32 alive; - B32 *open_flag; // e.g. &app->show_settings_window - PlatformWindow *platform_window; - Renderer *renderer; - UI_Context *ui_ctx; - UI_WidgetState wstate; + B32 alive; + B32 *open_flag; // e.g. &app->show_settings_window + PlatformWindow *platform_window; + Renderer *renderer; + UI_Context *ui_ctx; + UI_WidgetState wstate; UI_WindowContentFn content_fn; - void *content_user_data; - S32 width, height; - S32 last_w, last_h; - const char *title; + void *content_user_data; + S32 width, height; + S32 last_w, last_h; + const char *title; }; PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer, const char *title, B32 *open_flag, S32 width, S32 height, UI_WindowContentFn content_fn, void *user_data, - PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_POPUP, - B32 independent = 0); + PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_POPUP, + B32 independent = 0); void popup_close(PopupWindow *popup); PopupWindow *popup_find_by_flag(B32 *flag); void popup_do_frame(PopupWindow *popup, F32 dt); diff --git a/src/ui/ui_widgets.cpp b/src/ui/ui_widgets.cpp index 7444758..9b73a3e 100644 --- a/src/ui/ui_widgets.cpp +++ b/src/ui/ui_widgets.cpp @@ -5,39 +5,39 @@ // AFTER a CLAY() block, use Clay_PointerOver(elementId). #include "ui/ui_widgets.h" -#include +#include #include #include -#include +#include UI_WidgetState g_wstate = {}; // Icon per-frame pool (forward declaration for begin_frame) #define UI_MAX_ICONS_PER_FRAME 32 static CustomIconData g_icon_pool[UI_MAX_ICONS_PER_FRAME]; -static S32 g_icon_pool_count = 0; +static S32 g_icon_pool_count = 0; // Gradient per-frame pool (for button/title bar/dropdown/input gradients) #define UI_MAX_GRADIENTS_PER_FRAME 64 static CustomGradientData g_grad_pool[UI_MAX_GRADIENTS_PER_FRAME]; -static S32 g_grad_pool_count = 0; +static S32 g_grad_pool_count = 0; // Rotated icon per-frame pool (for knobs) #define UI_MAX_ROTATED_ICONS_PER_FRAME 16 static CustomRotatedIconData g_rotated_icon_pool[UI_MAX_ROTATED_ICONS_PER_FRAME]; -static S32 g_rotated_icon_pool_count = 0; +static S32 g_rotated_icon_pool_count = 0; // Static buffer pool for knob value text #define UI_MAX_KNOB_TEXT_BUFS 16 static char g_knob_text_bufs[UI_MAX_KNOB_TEXT_BUFS][32]; -static S32 g_knob_text_buf_count = 0; +static S32 g_knob_text_buf_count = 0; static CustomGradientData *alloc_gradient(Clay_Color top, Clay_Color bottom) { if (g_grad_pool_count >= UI_MAX_GRADIENTS_PER_FRAME) return nullptr; CustomGradientData *g = &g_grad_pool[g_grad_pool_count++]; - g->type = CUSTOM_RENDER_VGRADIENT; - g->top_color = top; - g->bottom_color = bottom; + g->type = CUSTOM_RENDER_VGRADIENT; + g->top_color = top; + g->bottom_color = bottom; return g; } @@ -68,53 +68,47 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius, // Draw outermost first (largest, lowest alpha contribution), innermost last for (S32 i = SHADOW_LAYERS - 1; i >= 0; i--) { - F32 t = (F32)(i + 1) / (F32)SHADOW_LAYERS; // 1/N .. 1.0 + F32 t = (F32)(i + 1) / (F32)SHADOW_LAYERS; // 1/N .. 1.0 F32 expand = radius * t; - S32 sid = g_shadow_id_counter++; + S32 sid = g_shadow_id_counter++; Clay_ElementId shadow_eid = CLAY_IDI("Shadow", sid); if (attach_to_root) { CLAY(shadow_eid, - .layout = { - .sizing = { - .width = CLAY_SIZING_FIXED(bb.width + expand*2), - .height = CLAY_SIZING_FIXED(bb.height + expand*2), - }, - }, - .backgroundColor = {0, 0, 0, per_layer}, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand), - .floating = { - .offset = { bb.x - expand + ox, bb.y - expand + oy }, - .zIndex = z, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .attachTo = CLAY_ATTACH_TO_ROOT, - } - ) {} + .layout = { + .sizing = { + .width = CLAY_SIZING_FIXED(bb.width + expand * 2), + .height = CLAY_SIZING_FIXED(bb.height + expand * 2), + }, + }, + .backgroundColor = { 0, 0, 0, per_layer }, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand), .floating = { + .offset = { bb.x - expand + ox, bb.y - expand + oy }, + .zIndex = z, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .attachTo = CLAY_ATTACH_TO_ROOT, + }) {} } else { CLAY(shadow_eid, - .layout = { - .sizing = { - .width = CLAY_SIZING_FIXED(bb.width + expand*2), - .height = CLAY_SIZING_FIXED(bb.height + expand*2), - }, - }, - .backgroundColor = {0, 0, 0, per_layer}, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand), - .floating = { - .offset = { -expand + ox, -expand + oy }, - .parentId = parent_id, - .zIndex = z, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = parent_attach, - }, - .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, - } - ) {} + .layout = { + .sizing = { + .width = CLAY_SIZING_FIXED(bb.width + expand * 2), + .height = CLAY_SIZING_FIXED(bb.height + expand * 2), + }, + }, + .backgroundColor = { 0, 0, 0, per_layer }, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand), .floating = { + .offset = { -expand + ox, -expand + oy }, + .parentId = parent_id, + .zIndex = z, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = parent_attach, + }, + .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, + }) {} } } } @@ -124,13 +118,13 @@ void ui_widgets_init() { } void ui_widgets_begin_frame(PlatformInput input) { - g_wstate.input = input; - g_wstate.mouse_clicked = (input.mouse_down && !input.was_mouse_down); - g_icon_pool_count = 0; - g_grad_pool_count = 0; + g_wstate.input = input; + g_wstate.mouse_clicked = (input.mouse_down && !input.was_mouse_down); + g_icon_pool_count = 0; + g_grad_pool_count = 0; g_rotated_icon_pool_count = 0; - g_knob_text_buf_count = 0; - g_shadow_id_counter = 0; + g_knob_text_buf_count = 0; + g_shadow_id_counter = 0; g_frame_number++; // Release knob drag if mouse is up @@ -139,7 +133,7 @@ void ui_widgets_begin_frame(PlatformInput input) { } g_wstate.cursor_blink += 1.0f / 60.0f; g_wstate.text_input_count = 0; - g_wstate.tab_pressed = 0; + g_wstate.tab_pressed = 0; // Detect Tab key press this frame for (S32 k = 0; k < input.key_count; k++) { @@ -161,7 +155,7 @@ static Clay_TextElementConfig g_widget_text_config_sel; static Clay_TextElementConfig g_widget_text_config_btn; static Clay_TextElementConfig g_widget_text_config_tab; static Clay_TextElementConfig g_widget_text_config_tab_inactive; -static F32 g_widget_text_configs_scale = 0; +static F32 g_widget_text_configs_scale = 0; void ui_widgets_theme_changed() { g_widget_text_configs_scale = 0; @@ -171,52 +165,52 @@ static void ensure_widget_text_configs() { if (g_widget_text_configs_scale == g_ui_scale) return; g_widget_text_configs_scale = g_ui_scale; - g_widget_text_config = {}; + g_widget_text_config = {}; g_widget_text_config.textColor = g_theme.text; - g_widget_text_config.fontSize = FONT_SIZE_NORMAL; - g_widget_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; - g_widget_text_config_dim = {}; + g_widget_text_config_dim = {}; g_widget_text_config_dim.textColor = g_theme.text_dim; - g_widget_text_config_dim.fontSize = FONT_SIZE_NORMAL; - g_widget_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config_dim.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; // Selected text: white on accent background (background set on parent element) - g_widget_text_config_sel = {}; - g_widget_text_config_sel.textColor = Clay_Color{255, 255, 255, 255}; - g_widget_text_config_sel.fontSize = FONT_SIZE_NORMAL; - g_widget_text_config_sel.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config_sel = {}; + g_widget_text_config_sel.textColor = Clay_Color{ 255, 255, 255, 255 }; + g_widget_text_config_sel.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config_sel.wrapMode = CLAY_TEXT_WRAP_NONE; // Button text (always readable on accent background) - g_widget_text_config_btn = {}; + g_widget_text_config_btn = {}; g_widget_text_config_btn.textColor = g_theme.button_text; - g_widget_text_config_btn.fontSize = FONT_SIZE_NORMAL; - g_widget_text_config_btn.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config_btn.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config_btn.wrapMode = CLAY_TEXT_WRAP_NONE; // Tab text (always light — readable on colored tab gradient) - g_widget_text_config_tab = {}; + g_widget_text_config_tab = {}; g_widget_text_config_tab.textColor = g_theme.tab_text; - g_widget_text_config_tab.fontSize = FONT_SIZE_TAB; - g_widget_text_config_tab.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config_tab.fontSize = FONT_SIZE_TAB; + g_widget_text_config_tab.wrapMode = CLAY_TEXT_WRAP_NONE; // Inactive tab text (theme text color, smaller font) - g_widget_text_config_tab_inactive = {}; + g_widget_text_config_tab_inactive = {}; g_widget_text_config_tab_inactive.textColor = g_theme.text; - g_widget_text_config_tab_inactive.fontSize = FONT_SIZE_TAB; - g_widget_text_config_tab_inactive.wrapMode = CLAY_TEXT_WRAP_NONE; + g_widget_text_config_tab_inactive.fontSize = FONT_SIZE_TAB; + g_widget_text_config_tab_inactive.wrapMode = CLAY_TEXT_WRAP_NONE; } static Clay_String clay_str(const char *s) { - Clay_String r = {}; + Clay_String r = {}; r.isStaticallyAllocated = false; - r.length = (S32)strlen(s); - r.chars = s; + r.length = (S32)strlen(s); + r.chars = s; return r; } // Build Clay_ElementId from runtime string (CLAY_ID requires string literals) -#define WID(s) CLAY_SID(clay_str(s)) -#define WIDI(s, i) CLAY_SIDI(clay_str(s), i) +#define WID(s) CLAY_SID(clay_str(s)) +#define WIDI(s, i) CLAY_SIDI(clay_str(s), i) //////////////////////////////// // Icon @@ -224,18 +218,17 @@ static Clay_String clay_str(const char *s) { void ui_icon(UI_IconID icon, F32 size, Clay_Color color) { if (g_icon_pool_count >= UI_MAX_ICONS_PER_FRAME) return; - S32 idx = g_icon_pool_count; + S32 idx = g_icon_pool_count; CustomIconData *data = &g_icon_pool[g_icon_pool_count++]; - data->type = CUSTOM_RENDER_ICON; - data->icon_id = (S32)icon; - data->color = color; + data->type = CUSTOM_RENDER_ICON; + data->icon_id = (S32)icon; + data->color = color; CLAY(CLAY_IDI("UIIcon", idx), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) }, - }, - .custom = { .customData = data } - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) }, + }, + .custom = { .customData = data }) {} } //////////////////////////////// @@ -244,11 +237,10 @@ void ui_icon(UI_IconID icon, F32 size, Clay_Color color) { void ui_label(const char *id, const char *text) { ensure_widget_text_configs(); CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .padding = { 0, 0, uip(2), uip(2) }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .padding = { 0, 0, uip(2), uip(2) }, + }) { CLAY_TEXT(clay_str(text), &g_widget_text_config); } } @@ -259,24 +251,21 @@ void ui_label(const char *id, const char *text) { B32 ui_button(const char *id, const char *text) { ensure_widget_text_configs(); - Clay_ElementId eid = WID(id); - B32 hovered = Clay_PointerOver(eid); + Clay_ElementId eid = WID(id); + B32 hovered = Clay_PointerOver(eid); - Clay_Color base = hovered ? g_theme.accent_hover : g_theme.accent; - Clay_Color top = {(F32)Min((S32)base.r+12,255), (F32)Min((S32)base.g+12,255), (F32)Min((S32)base.b+12,255), base.a}; - Clay_Color bot = {(F32)Max((S32)base.r-15,0), (F32)Max((S32)base.g-15,0), (F32)Max((S32)base.b-15,0), base.a}; + Clay_Color base = hovered ? g_theme.accent_hover : g_theme.accent; + Clay_Color top = { (F32)Min((S32)base.r + 12, 255), (F32)Min((S32)base.g + 12, 255), (F32)Min((S32)base.b + 12, 255), base.a }; + Clay_Color bot = { (F32)Max((S32)base.r - 15, 0), (F32)Max((S32)base.g - 15, 0), (F32)Max((S32)base.b - 15, 0), base.a }; CustomGradientData *grad = alloc_gradient(top, bot); CLAY(eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(WIDGET_BUTTON_HEIGHT) }, - .padding = { uip(12), uip(12), uip(1), 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = base, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .custom = { .customData = grad }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(WIDGET_BUTTON_HEIGHT) }, + .padding = { uip(12), uip(12), uip(1), 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = base, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .custom = { .customData = grad }, ) { CLAY_TEXT(clay_str(text), &g_widget_text_config_btn); } @@ -290,31 +279,27 @@ B32 ui_checkbox(const char *id, const char *label, B32 *value) { ensure_widget_text_configs(); B32 changed = 0; - Clay_ElementId eid = WID(id); - B32 hovered = Clay_PointerOver(eid); + Clay_ElementId eid = WID(id); + B32 hovered = Clay_PointerOver(eid); CLAY(eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_HEIGHT) }, - .childGap = uip(8), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_HEIGHT) }, + .childGap = uip(8), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Box Clay_Color box_bg = *value ? g_theme.accent : g_theme.bg_dark; if (hovered) { box_bg = *value ? g_theme.accent_hover : g_theme.bg_lighter; } CLAY(WIDI(id, 1), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_SIZE), .height = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_SIZE) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = box_bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_SIZE), .height = CLAY_SIZING_FIXED(WIDGET_CHECKBOX_SIZE) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = box_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }) { if (*value) { ui_icon(UI_ICON_CHECK, WIDGET_CHECKBOX_SIZE * 0.75f, g_theme.button_text); } @@ -325,7 +310,7 @@ B32 ui_checkbox(const char *id, const char *label, B32 *value) { } if (hovered && g_wstate.mouse_clicked) { - *value = !(*value); + *value = !(*value); changed = 1; } @@ -340,47 +325,40 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte B32 changed = 0; CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = uip(4), - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = uip(4), + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { for (S32 i = 0; i < count; i++) { - B32 is_selected = (*selected == i); - Clay_ElementId row_id = WIDI(id, i + 100); - B32 row_hovered = Clay_PointerOver(row_id); + B32 is_selected = (*selected == i); + Clay_ElementId row_id = WIDI(id, i + 100); + B32 row_hovered = Clay_PointerOver(row_id); CLAY(row_id, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(26)) }, - .childGap = uip(8), - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(uis(26)) }, + .childGap = uip(8), + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }) { // Radio circle Clay_Color dot_bg = is_selected ? g_theme.accent : g_theme.bg_dark; if (row_hovered) { dot_bg = is_selected ? g_theme.accent_hover : g_theme.bg_lighter; } CLAY(WIDI(id, i + 200), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_RADIO_OUTER), .height = CLAY_SIZING_FIXED(WIDGET_RADIO_OUTER) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = dot_bg, - .cornerRadius = CLAY_CORNER_RADIUS(uis(8)), - .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_RADIO_OUTER), .height = CLAY_SIZING_FIXED(WIDGET_RADIO_OUTER) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = dot_bg, .cornerRadius = CLAY_CORNER_RADIUS(uis(8)), .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }) { if (is_selected) { CLAY(WIDI(id, i + 300), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_RADIO_INNER), .height = CLAY_SIZING_FIXED(WIDGET_RADIO_INNER) }, - }, - .backgroundColor = g_theme.button_text, - .cornerRadius = CLAY_CORNER_RADIUS(uis(4)) - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(WIDGET_RADIO_INNER), .height = CLAY_SIZING_FIXED(WIDGET_RADIO_INNER) }, + }, + .backgroundColor = g_theme.button_text, .cornerRadius = CLAY_CORNER_RADIUS(uis(4))) {} } } @@ -390,7 +368,7 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte if (row_hovered && g_wstate.mouse_clicked && !is_selected) { *selected = i; - changed = 1; + changed = 1; } } } @@ -406,7 +384,7 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte // MAX_TEXT_INPUTS simultaneous text inputs per frame. // Each text input may need up to 3 display buffers (before/sel/after). -#define MAX_TEXT_INPUTS 8 +#define MAX_TEXT_INPUTS 8 #define DISPLAY_BUF_SIZE 512 // 3 buffers per input: [0]=before selection, [1]=selected text, [2]=after selection static char g_text_display_bufs[MAX_TEXT_INPUTS * 3][DISPLAY_BUF_SIZE]; @@ -420,8 +398,13 @@ void ui_text_input_reset_display_bufs() { static void text_input_get_sel(S32 *lo, S32 *hi) { S32 a = g_wstate.sel_start; S32 b = g_wstate.sel_end; - if (a <= b) { *lo = a; *hi = b; } - else { *lo = b; *hi = a; } + if (a <= b) { + *lo = a; + *hi = b; + } else { + *lo = b; + *hi = a; + } } // Helper: true if there's an active selection @@ -437,8 +420,8 @@ static S32 text_input_delete_sel(char *buf, S32 len) { if (lo == hi) return len; memmove(&buf[lo], &buf[hi], len - hi + 1); g_wstate.cursor_pos = lo; - g_wstate.sel_start = lo; - g_wstate.sel_end = lo; + g_wstate.sel_start = lo; + g_wstate.sel_end = lo; return len - (hi - lo); } @@ -450,7 +433,7 @@ static void text_input_copy_sel(const char *buf, S32 len) { if (hi > len) hi = len; // Use a temp buffer to null-terminate the selection char tmp[DISPLAY_BUF_SIZE]; - S32 sel_len = hi - lo; + S32 sel_len = hi - lo; if (sel_len > (S32)sizeof(tmp) - 1) sel_len = (S32)sizeof(tmp) - 1; memcpy(tmp, &buf[lo], sel_len); tmp[sel_len] = '\0'; @@ -475,7 +458,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { char *dbuf_sel = g_text_display_bufs[my_buf_base + 1]; char *dbuf_after = g_text_display_bufs[my_buf_base + 2]; - B32 hovered = Clay_PointerOver(eid); + B32 hovered = Clay_PointerOver(eid); B32 is_focused = (g_wstate.focused_id == eid.id); // Tab cycling: if Tab was pressed and we're focused, move to next input. @@ -491,26 +474,26 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { if (g_wstate.mouse_clicked) { if (hovered) { if (!is_focused) { - g_wstate.focused_id = eid.id; - g_wstate.cursor_pos = (S32)strlen(buf); - g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.focused_id = eid.id; + g_wstate.cursor_pos = (S32)strlen(buf); + g_wstate.sel_start = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; g_wstate.cursor_blink = 0; - is_focused = 1; + is_focused = 1; } else { // Already focused, clicking clears selection g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; } } else if (is_focused) { g_wstate.focused_id = 0; - is_focused = 0; + is_focused = 0; } } // Process keyboard input if focused if (is_focused) { - S32 len = (S32)strlen(buf); + S32 len = (S32)strlen(buf); B32 ctrl = g_wstate.input.ctrl_held; // Clamp cursor and selection @@ -530,8 +513,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { if (ctrl) { if (key == PKEY_A) { // Select all - g_wstate.sel_start = 0; - g_wstate.sel_end = len; + g_wstate.sel_start = 0; + g_wstate.sel_end = len; g_wstate.cursor_pos = len; continue; } @@ -544,7 +527,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { // Cut if (text_input_has_sel()) { text_input_copy_sel(buf, len); - len = text_input_delete_sel(buf, len); + len = text_input_delete_sel(buf, len); text_changed = 1; } continue; @@ -560,7 +543,10 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { S32 clip_len = (S32)strlen(clip); // Filter to single line (stop at newline) for (S32 i = 0; i < clip_len; i++) { - if (clip[i] == '\n' || clip[i] == '\r') { clip_len = i; break; } + if (clip[i] == '\n' || clip[i] == '\r') { + clip_len = i; + break; + } } S32 space = buf_size - 1 - len; if (clip_len > space) clip_len = space; @@ -572,7 +558,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { text_changed = 1; } g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; } continue; } @@ -583,7 +569,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { switch (key) { case PKEY_BACKSPACE: if (text_input_has_sel()) { - len = text_input_delete_sel(buf, len); + len = text_input_delete_sel(buf, len); text_changed = 1; } else if (g_wstate.cursor_pos > 0) { memmove(&buf[g_wstate.cursor_pos - 1], &buf[g_wstate.cursor_pos], len - g_wstate.cursor_pos + 1); @@ -592,11 +578,11 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { text_changed = 1; } g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; break; case PKEY_DELETE: if (text_input_has_sel()) { - len = text_input_delete_sel(buf, len); + len = text_input_delete_sel(buf, len); text_changed = 1; } else if (g_wstate.cursor_pos < len) { memmove(&buf[g_wstate.cursor_pos], &buf[g_wstate.cursor_pos + 1], len - g_wstate.cursor_pos); @@ -604,46 +590,48 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { text_changed = 1; } g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; break; case PKEY_LEFT: if (text_input_has_sel()) { - S32 lo, hi; text_input_get_sel(&lo, &hi); + S32 lo, hi; + text_input_get_sel(&lo, &hi); g_wstate.cursor_pos = lo; } else if (g_wstate.cursor_pos > 0) { g_wstate.cursor_pos--; } - g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_start = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; g_wstate.cursor_blink = 0; break; case PKEY_RIGHT: if (text_input_has_sel()) { - S32 lo, hi; text_input_get_sel(&lo, &hi); + S32 lo, hi; + text_input_get_sel(&lo, &hi); g_wstate.cursor_pos = hi; } else if (g_wstate.cursor_pos < len) { g_wstate.cursor_pos++; } - g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_start = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; g_wstate.cursor_blink = 0; break; case PKEY_HOME: - g_wstate.cursor_pos = 0; - g_wstate.sel_start = 0; - g_wstate.sel_end = 0; + g_wstate.cursor_pos = 0; + g_wstate.sel_start = 0; + g_wstate.sel_end = 0; g_wstate.cursor_blink = 0; break; case PKEY_END: - g_wstate.cursor_pos = len; - g_wstate.sel_start = len; - g_wstate.sel_end = len; + g_wstate.cursor_pos = len; + g_wstate.sel_start = len; + g_wstate.sel_end = len; g_wstate.cursor_blink = 0; break; case PKEY_RETURN: case PKEY_ESCAPE: g_wstate.focused_id = 0; - is_focused = 0; + is_focused = 0; break; } } @@ -662,11 +650,11 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { buf[g_wstate.cursor_pos] = (char)ch; g_wstate.cursor_pos++; len++; - text_changed = 1; + text_changed = 1; g_wstate.cursor_blink = 0; } g_wstate.sel_start = g_wstate.cursor_pos; - g_wstate.sel_end = g_wstate.cursor_pos; + g_wstate.sel_end = g_wstate.cursor_pos; } } } @@ -678,7 +666,10 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { // our ID was just added above. Find it and advance to next. S32 my_idx = -1; for (S32 i = 0; i < g_wstate.text_input_count; i++) { - if (g_wstate.text_input_ids[i] == eid.id) { my_idx = i; break; } + if (g_wstate.text_input_ids[i] == eid.id) { + my_idx = i; + break; + } } if (my_idx >= 0) { // Focus next (wrapping). But we might not have all inputs registered @@ -700,14 +691,14 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { g_wstate.focused_id = g_wstate.text_input_ids[0]; } g_wstate.cursor_pos = 0; - g_wstate.sel_start = 0; - g_wstate.sel_end = 0; - is_focused = 0; // no longer focused on THIS input + g_wstate.sel_start = 0; + g_wstate.sel_end = 0; + is_focused = 0; // no longer focused on THIS input } } // Build display - S32 len = (S32)strlen(buf); + S32 len = (S32)strlen(buf); S32 sel_lo = 0, sel_hi = 0; B32 has_sel = 0; if (is_focused) { @@ -717,26 +708,22 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { has_sel = (sel_lo != sel_hi); } - Clay_Color bg = is_focused ? g_theme.bg_dark : g_theme.bg_medium; + Clay_Color bg = is_focused ? g_theme.bg_dark : g_theme.bg_medium; Clay_Color border_color = is_focused ? g_theme.accent : g_theme.border; // Inset effect: darker top, lighter bottom (recessed look) - Clay_Color inset_top = {(F32)Max((S32)bg.r-8,0), (F32)Max((S32)bg.g-8,0), (F32)Max((S32)bg.b-8,0), bg.a}; - Clay_Color inset_bot = {(F32)Min((S32)bg.r+3,255), (F32)Min((S32)bg.g+3,255), (F32)Min((S32)bg.b+3,255), bg.a}; + Clay_Color inset_top = { (F32)Max((S32)bg.r - 8, 0), (F32)Max((S32)bg.g - 8, 0), (F32)Max((S32)bg.b - 8, 0), bg.a }; + Clay_Color inset_bot = { (F32)Min((S32)bg.r + 3, 255), (F32)Min((S32)bg.g + 3, 255), (F32)Min((S32)bg.b + 3, 255), bg.a }; CustomGradientData *inset_grad = alloc_gradient(inset_top, inset_bot); CLAY(eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_INPUT_HEIGHT) }, - .padding = { uip(8), uip(8), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .custom = { .customData = inset_grad }, - .border = { .color = border_color, .width = { 1, 1, 1, 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_INPUT_HEIGHT) }, + .padding = { uip(8), uip(8), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .custom = { .customData = inset_grad }, .border = { .color = border_color, .width = { 1, 1, 1, 1 } }, ) { if (len == 0 && !is_focused) { // Placeholder CLAY_TEXT(CLAY_STRING("..."), &g_widget_text_config_dim); @@ -756,11 +743,10 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { memcpy(dbuf_sel, &buf[sel_lo], slen); dbuf_sel[slen] = '\0'; CLAY(WIDI(id, 900), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - }, - .backgroundColor = g_theme.accent - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + }, + .backgroundColor = g_theme.accent) { CLAY_TEXT(clay_str(dbuf_sel), &g_widget_text_config_sel); } } @@ -782,7 +768,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) { S32 before = cp < total ? cp : total; memcpy(dbuf_before, buf, before); dbuf_before[before] = '|'; - S32 after = total - before - 1; + S32 after = total - before - 1; if (after > 0) memcpy(dbuf_before + before + 1, buf + cp, after); dbuf_before[total] = '\0'; CLAY_TEXT(clay_str(dbuf_before), &g_widget_text_config); @@ -805,34 +791,37 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) ensure_widget_text_configs(); B32 changed = 0; - Clay_ElementId eid = WID(id); - B32 header_hovered = Clay_PointerOver(eid); - B32 is_open = (g_wstate.open_dropdown_id == eid.id); + Clay_ElementId eid = WID(id); + B32 header_hovered = Clay_PointerOver(eid); + B32 is_open = (g_wstate.open_dropdown_id == eid.id); // Display current selection — truncate with "..." if it overflows the header const char *current_label = (*selected >= 0 && *selected < count) ? options[*selected] : "Select..."; - static char dd_trunc_buf[256]; - const char *display_label = current_label; - Clay_ElementId text_eid = WIDI(id, 500); - F32 avail_w = Clay_GetElementData(text_eid).boundingBox.width; + static char dd_trunc_buf[256]; + const char *display_label = current_label; + Clay_ElementId text_eid = WIDI(id, 500); + F32 avail_w = Clay_GetElementData(text_eid).boundingBox.width; if (avail_w > 0) { - S32 label_len = (S32)strlen(current_label); + S32 label_len = (S32)strlen(current_label); Vec2F32 text_size = ui_measure_text(current_label, label_len, FONT_SIZE_NORMAL); if (text_size.x > avail_w) { - Vec2F32 dots = ui_measure_text("...", 3, FONT_SIZE_NORMAL); - F32 target_w = avail_w - dots.x; - S32 lo = 0, hi = label_len; + Vec2F32 dots = ui_measure_text("...", 3, FONT_SIZE_NORMAL); + F32 target_w = avail_w - dots.x; + S32 lo = 0, hi = label_len; while (lo < hi) { - S32 mid = (lo + hi + 1) / 2; + S32 mid = (lo + hi + 1) / 2; Vec2F32 seg = ui_measure_text(current_label, mid, FONT_SIZE_NORMAL); - if (seg.x <= target_w) lo = mid; else hi = mid - 1; + if (seg.x <= target_w) lo = mid; + else hi = mid - 1; } if (lo + 3 < (S32)sizeof(dd_trunc_buf)) { memcpy(dd_trunc_buf, current_label, lo); - dd_trunc_buf[lo] = '.'; dd_trunc_buf[lo+1] = '.'; dd_trunc_buf[lo+2] = '.'; - dd_trunc_buf[lo+3] = '\0'; - display_label = dd_trunc_buf; + dd_trunc_buf[lo] = '.'; + dd_trunc_buf[lo + 1] = '.'; + dd_trunc_buf[lo + 2] = '.'; + dd_trunc_buf[lo + 3] = '\0'; + display_label = dd_trunc_buf; } } } @@ -840,35 +829,29 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) Clay_Color bg = is_open ? g_theme.bg_dark : g_theme.bg_medium; if (header_hovered && !is_open) bg = g_theme.bg_lighter; - Clay_Color dd_top = {(F32)Min((S32)bg.r+6,255), (F32)Min((S32)bg.g+6,255), (F32)Min((S32)bg.b+6,255), bg.a}; + Clay_Color dd_top = { (F32)Min((S32)bg.r + 6, 255), (F32)Min((S32)bg.g + 6, 255), (F32)Min((S32)bg.b + 6, 255), bg.a }; CustomGradientData *dd_grad = alloc_gradient(dd_top, bg); CLAY(eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_DROPDOWN_HEIGHT) }, - .padding = { uip(8), uip(8), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = bg, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .custom = { .customData = dd_grad }, - .border = { .color = is_open ? g_theme.accent : g_theme.border, .width = { 1, 1, 1, 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_DROPDOWN_HEIGHT) }, + .padding = { uip(8), uip(8), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .custom = { .customData = dd_grad }, .border = { .color = is_open ? g_theme.accent : g_theme.border, .width = { 1, 1, 1, 1 } }, ) { CLAY(text_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + }) { CLAY_TEXT(clay_str(display_label), &g_widget_text_config); } CLAY(WIDI(id, 501), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(uis(20)), .height = CLAY_SIZING_FIXED(uis(20)) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(uis(20)), .height = CLAY_SIZING_FIXED(uis(20)) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }) { ui_icon(UI_ICON_CHEVRON_DOWN, uis(12), g_theme.text_dim); } } @@ -877,17 +860,17 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) if (header_hovered && g_wstate.mouse_clicked) { if (is_open) { g_wstate.open_dropdown_id = 0; - is_open = 0; + is_open = 0; } else { g_wstate.open_dropdown_id = eid.id; - is_open = 1; + is_open = 1; } } // Draw dropdown list if open (floating so it escapes modal/container clipping) if (is_open) { - Clay_ElementId list_id = WIDI(id, 502); - F32 header_width = Clay_GetElementData(eid).boundingBox.width; + Clay_ElementId list_id = WIDI(id, 502); + F32 header_width = Clay_GetElementData(eid).boundingBox.width; // Dropdown list shadow { @@ -898,53 +881,55 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) } CLAY(list_id, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(.min = header_width), .height = CLAY_SIZING_FIT() }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - .floating = { - .parentId = eid.id, - .zIndex = 2000, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM, - }, - .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, - }, - .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(.min = header_width), .height = CLAY_SIZING_FIT() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .floating = { + .parentId = eid.id, + .zIndex = 2000, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM, + }, + .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, + }, + .border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }, ) { for (S32 i = 0; i < count; i++) { - B32 is_item_selected = (*selected == i); - Clay_ElementId item_id = WIDI(id, i + 600); - B32 item_hovered = Clay_PointerOver(item_id); + B32 is_item_selected = (*selected == i); + Clay_ElementId item_id = WIDI(id, i + 600); + B32 item_hovered = Clay_PointerOver(item_id); Clay_Color item_bg = is_item_selected ? g_theme.accent : g_theme.bg_dark; if (item_hovered) item_bg = g_theme.bg_lighter; Clay_TextElementConfig *item_text = (is_item_selected && !item_hovered) - ? &g_widget_text_config_btn : &g_widget_text_config; + ? &g_widget_text_config_btn + : &g_widget_text_config; Clay_CornerRadius item_radius = {}; - if (i == 0) { item_radius.topLeft = CORNER_RADIUS; item_radius.topRight = CORNER_RADIUS; } - if (i == count - 1) { item_radius.bottomLeft = CORNER_RADIUS; item_radius.bottomRight = CORNER_RADIUS; } + if (i == 0) { + item_radius.topLeft = CORNER_RADIUS; + item_radius.topRight = CORNER_RADIUS; + } + if (i == count - 1) { + item_radius.bottomLeft = CORNER_RADIUS; + item_radius.bottomRight = CORNER_RADIUS; + } CLAY(item_id, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_DROPDOWN_ITEM_H) }, - .padding = { uip(8), uip(8), 0, 0 }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = item_bg, - .cornerRadius = item_radius - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_DROPDOWN_ITEM_H) }, + .padding = { uip(8), uip(8), 0, 0 }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = item_bg, .cornerRadius = item_radius) { CLAY_TEXT(clay_str(options[i]), item_text); } if (item_hovered && g_wstate.mouse_clicked) { - *selected = i; - changed = 1; + *selected = i; + changed = 1; g_wstate.open_dropdown_id = 0; } } @@ -955,7 +940,10 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) // Check if any item was clicked (already handled above) B32 clicked_item = 0; for (S32 i = 0; i < count; i++) { - if (Clay_PointerOver(WIDI(id, i + 600))) { clicked_item = 1; break; } + if (Clay_PointerOver(WIDI(id, i + 600))) { + clicked_item = 1; + break; + } } if (!clicked_item) { g_wstate.open_dropdown_id = 0; @@ -972,52 +960,46 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) static CustomGradientData g_tab_gradient; S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { - g_tab_gradient.type = CUSTOM_RENDER_VGRADIENT; - g_tab_gradient.top_color = TAB_ACTIVE_TOP; + g_tab_gradient.type = CUSTOM_RENDER_VGRADIENT; + g_tab_gradient.top_color = TAB_ACTIVE_TOP; g_tab_gradient.bottom_color = TAB_ACTIVE_BOTTOM; - Clay_String id_str = clay_str(id); + Clay_String id_str = clay_str(id); Clay_ElementId row_eid = Clay__HashString(id_str, 0); CLAY(row_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, - .padding = { 0, 0, 0, 0 }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = g_theme.bg_medium, - .border = { .color = g_theme.border, .width = { .bottom = 1 } }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() }, + .padding = { 0, 0, 0, 0 }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = g_theme.bg_medium, .border = { .color = g_theme.border, .width = { .bottom = 1 } }, ) { for (S32 i = 0; i < count; i++) { - Clay_ElementId tab_eid = Clay__HashStringWithOffset(id_str, (U32)i, 0); - B32 is_active = (i == *selected); - B32 hovered = Clay_PointerOver(tab_eid); + Clay_ElementId tab_eid = Clay__HashStringWithOffset(id_str, (U32)i, 0); + B32 is_active = (i == *selected); + B32 hovered = Clay_PointerOver(tab_eid); Clay_String lbl_str = clay_str(labels[i]); if (is_active) { CLAY(tab_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) }, - .padding = { TAB_PADDING_H, TAB_PADDING_H, 0, uip(6) }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 }, - .custom = { .customData = &g_tab_gradient }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) }, + .padding = { TAB_PADDING_H, TAB_PADDING_H, 0, uip(6) }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 }, .custom = { .customData = &g_tab_gradient }, ) { CLAY_TEXT(lbl_str, &g_widget_text_config_tab); } } else { Clay_Color bg = hovered ? (Clay_Color)TAB_INACTIVE_HOVER : (Clay_Color)TAB_INACTIVE_BG; CLAY(tab_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) }, - .padding = { TAB_PADDING_H, TAB_PADDING_H, 0, uip(6) }, - .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = bg, - .cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 }, - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) }, + .padding = { TAB_PADDING_H, TAB_PADDING_H, 0, uip(6) }, + .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = bg, .cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 }, ) { CLAY_TEXT(lbl_str, &g_widget_text_config_tab_inactive); } } @@ -1036,23 +1018,27 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { // Process keyboard input for value text editing. // Returns 0 = still editing, 1 = committed, 2 = cancelled. static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *changed) { - char *ebuf = g_wstate.knob_edit_buf; - S32 elen = (S32)strlen(ebuf); - S32 *ecur = &g_wstate.knob_edit_cursor; - S32 *esel0 = &g_wstate.knob_edit_sel_start; - S32 *esel1 = &g_wstate.knob_edit_sel_end; - B32 commit = 0; - B32 cancel = 0; - B32 ctrl = g_wstate.input.ctrl_held; + char *ebuf = g_wstate.knob_edit_buf; + S32 elen = (S32)strlen(ebuf); + S32 *ecur = &g_wstate.knob_edit_cursor; + S32 *esel0 = &g_wstate.knob_edit_sel_start; + S32 *esel1 = &g_wstate.knob_edit_sel_end; + B32 commit = 0; + B32 cancel = 0; + B32 ctrl = g_wstate.input.ctrl_held; if (*ecur > elen) *ecur = elen; if (*esel0 > elen) *esel0 = elen; if (*esel1 > elen) *esel1 = elen; - #define KE_HAS_SEL() (*esel0 != *esel1) - #define KE_SEL_LO() (*esel0 < *esel1 ? *esel0 : *esel1) - #define KE_SEL_HI() (*esel0 < *esel1 ? *esel1 : *esel0) - #define KE_CLEAR_SEL() do { *esel0 = *ecur; *esel1 = *ecur; } while(0) +#define KE_HAS_SEL() (*esel0 != *esel1) +#define KE_SEL_LO() (*esel0 < *esel1 ? *esel0 : *esel1) +#define KE_SEL_HI() (*esel0 < *esel1 ? *esel1 : *esel0) +#define KE_CLEAR_SEL() \ + do { \ + *esel0 = *ecur; \ + *esel1 = *ecur; \ + } while (0) auto ke_delete_sel = [&]() -> S32 { S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); @@ -1068,22 +1054,31 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * if (ctrl) { if (key == PKEY_A) { - *esel0 = 0; *esel1 = elen; *ecur = elen; continue; + *esel0 = 0; + *esel1 = elen; + *ecur = elen; + continue; } if (key == PKEY_C) { if (KE_HAS_SEL()) { - S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); - char tmp[32]; S32 n = hi - lo; if (n > 31) n = 31; - memcpy(tmp, &ebuf[lo], n); tmp[n] = '\0'; + S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); + char tmp[32]; + S32 n = hi - lo; + if (n > 31) n = 31; + memcpy(tmp, &ebuf[lo], n); + tmp[n] = '\0'; platform_clipboard_set(tmp); } continue; } if (key == PKEY_X) { if (KE_HAS_SEL()) { - S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); - char tmp[32]; S32 n = hi - lo; if (n > 31) n = 31; - memcpy(tmp, &ebuf[lo], n); tmp[n] = '\0'; + S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); + char tmp[32]; + S32 n = hi - lo; + if (n > 31) n = 31; + memcpy(tmp, &ebuf[lo], n); + tmp[n] = '\0'; platform_clipboard_set(tmp); elen = ke_delete_sel(); } @@ -1093,8 +1088,9 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * const char *clip = platform_clipboard_get(); if (clip) { if (KE_HAS_SEL()) elen = ke_delete_sel(); - S32 clip_len = (S32)strlen(clip); - char filtered[32]; S32 flen = 0; + S32 clip_len = (S32)strlen(clip); + char filtered[32]; + S32 flen = 0; for (S32 i = 0; i < clip_len && flen < 30; i++) { char c = clip[i]; if ((c >= '0' && c <= '9') || c == '.' || c == '-') @@ -1105,7 +1101,8 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * if (flen > 0) { memmove(&ebuf[*ecur + flen], &ebuf[*ecur], elen - *ecur + 1); memcpy(&ebuf[*ecur], filtered, flen); - *ecur += flen; elen += flen; + *ecur += flen; + elen += flen; } KE_CLEAR_SEL(); } @@ -1114,61 +1111,77 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * continue; } - if (key == PKEY_RETURN) { commit = 1; } - else if (key == PKEY_ESCAPE) { cancel = 1; } - else if (key == PKEY_BACKSPACE) { - if (KE_HAS_SEL()) { elen = ke_delete_sel(); } - else if (*ecur > 0) { + if (key == PKEY_RETURN) { + commit = 1; + } else if (key == PKEY_ESCAPE) { + cancel = 1; + } else if (key == PKEY_BACKSPACE) { + if (KE_HAS_SEL()) { + elen = ke_delete_sel(); + } else if (*ecur > 0) { memmove(&ebuf[*ecur - 1], &ebuf[*ecur], elen - *ecur + 1); - (*ecur)--; elen--; + (*ecur)--; + elen--; } KE_CLEAR_SEL(); } else if (key == PKEY_DELETE) { - if (KE_HAS_SEL()) { elen = ke_delete_sel(); } - else if (*ecur < elen) { + if (KE_HAS_SEL()) { + elen = ke_delete_sel(); + } else if (*ecur < elen) { memmove(&ebuf[*ecur], &ebuf[*ecur + 1], elen - *ecur); elen--; } KE_CLEAR_SEL(); } else if (key == PKEY_LEFT) { - if (KE_HAS_SEL()) { *ecur = KE_SEL_LO(); } - else if (*ecur > 0) { (*ecur)--; } + if (KE_HAS_SEL()) { + *ecur = KE_SEL_LO(); + } else if (*ecur > 0) { + (*ecur)--; + } KE_CLEAR_SEL(); } else if (key == PKEY_RIGHT) { - if (KE_HAS_SEL()) { *ecur = KE_SEL_HI(); } - else if (*ecur < elen) { (*ecur)++; } + if (KE_HAS_SEL()) { + *ecur = KE_SEL_HI(); + } else if (*ecur < elen) { + (*ecur)++; + } KE_CLEAR_SEL(); } } if (!commit && !cancel) { for (S32 c = 0; c < g_wstate.input.char_count; c++) { - U16 ch = g_wstate.input.chars[c]; + U16 ch = g_wstate.input.chars[c]; B32 valid = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-'; if (valid) { if (KE_HAS_SEL()) elen = ke_delete_sel(); if (elen < 30) { memmove(&ebuf[*ecur + 1], &ebuf[*ecur], elen - *ecur + 1); - ebuf[*ecur] = (char)ch; (*ecur)++; elen++; + ebuf[*ecur] = (char)ch; + (*ecur)++; + elen++; } KE_CLEAR_SEL(); } } } - #undef KE_HAS_SEL - #undef KE_SEL_LO - #undef KE_SEL_HI - #undef KE_CLEAR_SEL +#undef KE_HAS_SEL +#undef KE_SEL_LO +#undef KE_SEL_HI +#undef KE_CLEAR_SEL if (commit) { - char *end = nullptr; - F32 parsed = strtof(ebuf, &end); + char *end = nullptr; + F32 parsed = strtof(ebuf, &end); if (end != ebuf) { F32 lo = is_signed ? -max_val : 0.0f; if (parsed < lo) parsed = lo; if (parsed > max_val) parsed = max_val; - if (parsed != *value) { *value = parsed; *changed = 1; } + if (parsed != *value) { + *value = parsed; + *changed = 1; + } } g_wstate.knob_edit_id = 0; return 1; @@ -1183,16 +1196,16 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * static void value_edit_render(U32 hash, F32 width) { Clay_ElementId edit_eid = CLAY_IDI("ValEdit", (S32)hash); - char *ebuf = g_wstate.knob_edit_buf; - S32 elen = (S32)strlen(ebuf); - S32 ecur = g_wstate.knob_edit_cursor; - S32 esel0 = g_wstate.knob_edit_sel_start; - S32 esel1 = g_wstate.knob_edit_sel_end; + char *ebuf = g_wstate.knob_edit_buf; + S32 elen = (S32)strlen(ebuf); + S32 ecur = g_wstate.knob_edit_cursor; + S32 esel0 = g_wstate.knob_edit_sel_start; + S32 esel1 = g_wstate.knob_edit_sel_end; if (ecur > elen) ecur = elen; if (esel0 > elen) esel0 = elen; if (esel1 > elen) esel1 = elen; - S32 sel_lo = esel0 < esel1 ? esel0 : esel1; - S32 sel_hi = esel0 < esel1 ? esel1 : esel0; + S32 sel_lo = esel0 < esel1 ? esel0 : esel1; + S32 sel_hi = esel0 < esel1 ? esel1 : esel0; B32 has_sel = (sel_lo != sel_hi); static char ke_dbuf_before[32]; @@ -1200,58 +1213,57 @@ static void value_edit_render(U32 hash, F32 width) { static char ke_dbuf_after[32]; static Clay_TextElementConfig edit_cfg = {}; - edit_cfg.textColor = g_theme.text; - edit_cfg.fontSize = FONT_SIZE_SMALL; - edit_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + edit_cfg.textColor = g_theme.text; + edit_cfg.fontSize = FONT_SIZE_SMALL; + edit_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; static Clay_TextElementConfig edit_sel_cfg = {}; - edit_sel_cfg.textColor = Clay_Color{255, 255, 255, 255}; - edit_sel_cfg.fontSize = FONT_SIZE_SMALL; - edit_sel_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + edit_sel_cfg.textColor = Clay_Color{ 255, 255, 255, 255 }; + edit_sel_cfg.fontSize = FONT_SIZE_SMALL; + edit_sel_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY(edit_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(width), .height = CLAY_SIZING_FIT() }, - .padding = { uip(2), uip(2), uip(1), uip(1) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(uis(2)), - .border = { .color = g_theme.accent, .width = { 1, 1, 1, 1 } } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(width), .height = CLAY_SIZING_FIT() }, + .padding = { uip(2), uip(2), uip(1), uip(1) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(uis(2)), .border = { .color = g_theme.accent, .width = { 1, 1, 1, 1 } }) { if (has_sel) { if (sel_lo > 0) { S32 n = sel_lo; - memcpy(ke_dbuf_before, ebuf, n); ke_dbuf_before[n] = '\0'; + memcpy(ke_dbuf_before, ebuf, n); + ke_dbuf_before[n] = '\0'; Clay_String s_before = { .length = n, .chars = ke_dbuf_before }; CLAY_TEXT(s_before, &edit_cfg); } { S32 n = sel_hi - sel_lo; - memcpy(ke_dbuf_sel, &ebuf[sel_lo], n); ke_dbuf_sel[n] = '\0'; + memcpy(ke_dbuf_sel, &ebuf[sel_lo], n); + ke_dbuf_sel[n] = '\0'; Clay_String s_sel = { .length = n, .chars = ke_dbuf_sel }; CLAY(CLAY_IDI("ValEditSel", (S32)hash), - .layout = { .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() } }, - .backgroundColor = g_theme.accent - ) { + .layout = { .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() } }, + .backgroundColor = g_theme.accent) { CLAY_TEXT(s_sel, &edit_sel_cfg); } } if (sel_hi < elen) { S32 n = elen - sel_hi; - memcpy(ke_dbuf_after, &ebuf[sel_hi], n); ke_dbuf_after[n] = '\0'; + memcpy(ke_dbuf_after, &ebuf[sel_hi], n); + ke_dbuf_after[n] = '\0'; Clay_String s_after = { .length = n, .chars = ke_dbuf_after }; CLAY_TEXT(s_after, &edit_cfg); } } else { static char edit_display[64]; - S32 di = 0; + S32 di = 0; for (S32 i = 0; i < elen + 1 && di < 62; i++) { if (i == ecur) edit_display[di++] = '|'; if (i < elen) edit_display[di++] = ebuf[i]; } - edit_display[di] = '\0'; + edit_display[di] = '\0'; Clay_String s_cursor = { .length = di, .chars = edit_display }; CLAY_TEXT(s_cursor, &edit_cfg); } @@ -1266,13 +1278,16 @@ static Clay_ElementId value_edit_eid(U32 hash) { // Handle click-outside to commit the edit. static void value_edit_click_away(Clay_ElementId eid, F32 *value, F32 max_val, B32 is_signed, B32 *changed) { if (g_wstate.mouse_clicked && !Clay_PointerOver(eid)) { - char *end = nullptr; - F32 parsed = strtof(g_wstate.knob_edit_buf, &end); + char *end = nullptr; + F32 parsed = strtof(g_wstate.knob_edit_buf, &end); if (end != g_wstate.knob_edit_buf) { F32 lo = is_signed ? -max_val : 0.0f; if (parsed < lo) parsed = lo; if (parsed > max_val) parsed = max_val; - if (parsed != *value) { *value = parsed; *changed = 1; } + if (parsed != *value) { + *value = parsed; + *changed = 1; + } } g_wstate.knob_edit_id = 0; } @@ -1281,23 +1296,26 @@ static void value_edit_click_away(Clay_ElementId eid, F32 *value, F32 max_val, B // Enter text edit mode for a widget. static void value_edit_enter(U32 hash, F32 value, B32 is_signed) { g_wstate.knob_edit_id = hash; - g_wstate.focused_id = 0; + g_wstate.focused_id = 0; if (is_signed) { snprintf(g_wstate.knob_edit_buf, sizeof(g_wstate.knob_edit_buf), "%+.1f", value); } else { snprintf(g_wstate.knob_edit_buf, sizeof(g_wstate.knob_edit_buf), "%.1f", value); } - S32 slen = (S32)strlen(g_wstate.knob_edit_buf); - g_wstate.knob_edit_cursor = slen; + S32 slen = (S32)strlen(g_wstate.knob_edit_buf); + g_wstate.knob_edit_cursor = slen; g_wstate.knob_edit_sel_start = 0; - g_wstate.knob_edit_sel_end = slen; + g_wstate.knob_edit_sel_end = slen; } // Normalize a value to [0,1] range. static F32 value_normalize(F32 value, F32 max_val, B32 is_signed) { F32 n; - if (is_signed) { n = (value + max_val) / (2.0f * max_val); } - else { n = value / max_val; } + if (is_signed) { + n = (value + max_val) / (2.0f * max_val); + } else { + n = value / max_val; + } if (n < 0.0f) n = 0.0f; if (n > 1.0f) n = 1.0f; return n; @@ -1307,8 +1325,11 @@ static F32 value_normalize(F32 value, F32 max_val, B32 is_signed) { static char *value_format_text(F32 value, B32 is_signed, S32 *out_len) { if (g_knob_text_buf_count >= UI_MAX_KNOB_TEXT_BUFS) return nullptr; char *buf = g_knob_text_bufs[g_knob_text_buf_count++]; - if (is_signed) { *out_len = snprintf(buf, 32, "%+.1f", value); } - else { *out_len = snprintf(buf, 32, "%.1f", value); } + if (is_signed) { + *out_len = snprintf(buf, 32, "%+.1f", value); + } else { + *out_len = snprintf(buf, 32, "%.1f", value); + } return buf; } @@ -1319,37 +1340,40 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s ensure_widget_text_configs(); F32 knob_size = WIDGET_KNOB_SIZE; - B32 changed = 0; + B32 changed = 0; F32 normalized = value_normalize(*value, max_val, is_signed); // Angle: 270-degree sweep, -135 to +135 degrees F32 deg_to_rad = 3.14159265f / 180.0f; - F32 angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; + F32 angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; - U32 knob_hash = Clay__HashString(clay_str(id), 0).id; - B32 is_editing = (editable && g_wstate.knob_edit_id == knob_hash); - UI_KnobDragState *kd = &g_wstate.knob_drag; + U32 knob_hash = Clay__HashString(clay_str(id), 0).id; + B32 is_editing = (editable && g_wstate.knob_edit_id == knob_hash); + UI_KnobDragState *kd = &g_wstate.knob_drag; // Drag interaction (vertical mouse delta) if (!is_editing && kd->dragging_id == knob_hash && g_wstate.input.mouse_down) { B32 shift_now = g_wstate.input.shift_held; if (shift_now != kd->was_shift) { - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; - kd->was_shift = shift_now; + kd->was_shift = shift_now; } - F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; + F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; F32 sensitivity = 200.0f * g_ui_scale; if (shift_now) sensitivity *= 5.0f; - F32 range = is_signed ? (2.0f * max_val) : max_val; + F32 range = is_signed ? (2.0f * max_val) : max_val; F32 new_val = kd->value_at_start + (dy / sensitivity) * range; - F32 lo = is_signed ? -max_val : 0.0f; + F32 lo = is_signed ? -max_val : 0.0f; if (new_val < lo) new_val = lo; if (new_val > max_val) new_val = max_val; - if (new_val != *value) { *value = new_val; changed = 1; } + if (new_val != *value) { + *value = new_val; + changed = 1; + } normalized = value_normalize(*value, max_val, is_signed); - angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; + angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; } // Text edit keyboard input @@ -1358,67 +1382,66 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s if (result) { is_editing = 0; normalized = value_normalize(*value, max_val, is_signed); - angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; + angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; } } // Format value text - S32 val_len = 0; + S32 val_len = 0; char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); // Layout: vertical column (knob → value text → label) CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = WIDGET_KNOB_LABEL_GAP, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = WIDGET_KNOB_LABEL_GAP, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { Clay_ElementId knob_eid = CLAY_IDI("KnobBg", (S32)knob_hash); - B32 hovered = Clay_PointerOver(knob_eid); + B32 hovered = Clay_PointerOver(knob_eid); CLAY(knob_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(knob_size), .height = CLAY_SIZING_FIXED(knob_size) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(knob_size / 2.0f) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(knob_size), .height = CLAY_SIZING_FIXED(knob_size) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(knob_size / 2.0f)) { if (g_rotated_icon_pool_count < UI_MAX_ROTATED_ICONS_PER_FRAME) { - S32 ri_idx = g_rotated_icon_pool_count; - CustomRotatedIconData *rdata = &g_rotated_icon_pool[g_rotated_icon_pool_count++]; - rdata->type = CUSTOM_RENDER_ROTATED_ICON; - rdata->icon_id = (S32)UI_ICON_KNOB; - rdata->color = g_theme.accent; - rdata->angle_rad = angle_rad; + S32 ri_idx = g_rotated_icon_pool_count; + CustomRotatedIconData *rdata = &g_rotated_icon_pool[g_rotated_icon_pool_count++]; + rdata->type = CUSTOM_RENDER_ROTATED_ICON; + rdata->icon_id = (S32)UI_ICON_KNOB; + rdata->color = g_theme.accent; + rdata->angle_rad = angle_rad; F32 icon_size = knob_size * 0.75f; CLAY(CLAY_IDI("KnobIcon", ri_idx), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(icon_size), .height = CLAY_SIZING_FIXED(icon_size) }, - }, - .custom = { .customData = rdata } - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(icon_size), .height = CLAY_SIZING_FIXED(icon_size) }, + }, + .custom = { .customData = rdata }) {} } } // Click: F64-click resets, single click starts drag if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) { - B32 is_double_click = (kd->last_click_id == knob_hash && + B32 is_double_click = (kd->last_click_id == knob_hash && (g_frame_number - kd->last_click_frame) < 20); - kd->last_click_id = knob_hash; + kd->last_click_id = knob_hash; kd->last_click_frame = g_frame_number; if (is_double_click) { - if (*value != default_val) { *value = default_val; changed = 1; } - normalized = value_normalize(*value, max_val, is_signed); - angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; + if (*value != default_val) { + *value = default_val; + changed = 1; + } + normalized = value_normalize(*value, max_val, is_signed); + angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; kd->last_click_id = 0; } else { - kd->dragging_id = knob_hash; - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->dragging_id = knob_hash; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; } } @@ -1428,22 +1451,21 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s value_edit_render(knob_hash, knob_size); value_edit_click_away(value_edit_eid(knob_hash), value, max_val, is_signed, &changed); } else if (val_text && val_len > 0) { - Clay_ElementId val_eid = CLAY_IDI("KnobVal", (S32)knob_hash); - B32 val_hovered = Clay_PointerOver(val_eid); + Clay_ElementId val_eid = CLAY_IDI("KnobVal", (S32)knob_hash); + B32 val_hovered = Clay_PointerOver(val_eid); - Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; + Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; static Clay_TextElementConfig knob_val_cfg = {}; - knob_val_cfg.textColor = g_theme.text; - knob_val_cfg.fontSize = FONT_SIZE_SMALL; - knob_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + knob_val_cfg.textColor = g_theme.text; + knob_val_cfg.fontSize = FONT_SIZE_SMALL; + knob_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY(val_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(knob_size), .height = CLAY_SIZING_FIT() }, - .padding = { uip(2), uip(2), uip(1), uip(1) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(knob_size), .height = CLAY_SIZING_FIT() }, + .padding = { uip(2), uip(2), uip(1), uip(1) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { CLAY_TEXT(val_str, &knob_val_cfg); } @@ -1465,11 +1487,11 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable) { ensure_widget_text_configs(); - B32 changed = 0; - F32 normalized = value_normalize(*value, max_val, is_signed); - U32 hash = Clay__HashString(clay_str(id), 0).id; - B32 is_editing = (editable && g_wstate.knob_edit_id == hash); - UI_KnobDragState *kd = &g_wstate.knob_drag; + B32 changed = 0; + F32 normalized = value_normalize(*value, max_val, is_signed); + U32 hash = Clay__HashString(clay_str(id), 0).id; + B32 is_editing = (editable && g_wstate.knob_edit_id == hash); + UI_KnobDragState *kd = &g_wstate.knob_drag; F32 track_w = WIDGET_SLIDER_H_WIDTH; F32 track_h = WIDGET_SLIDER_H_TRACK_H; @@ -1480,19 +1502,22 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 if (!is_editing && kd->dragging_id == hash && g_wstate.input.mouse_down) { B32 shift_now = g_wstate.input.shift_held; if (shift_now != kd->was_shift) { - kd->drag_start_x = g_wstate.input.mouse_pos.x; + kd->drag_start_x = g_wstate.input.mouse_pos.x; kd->value_at_start = *value; - kd->was_shift = shift_now; + kd->was_shift = shift_now; } - F32 dx = g_wstate.input.mouse_pos.x - kd->drag_start_x; // right = positive + F32 dx = g_wstate.input.mouse_pos.x - kd->drag_start_x; // right = positive F32 sensitivity = 200.0f * g_ui_scale; if (shift_now) sensitivity *= 5.0f; - F32 range = is_signed ? (2.0f * max_val) : max_val; + F32 range = is_signed ? (2.0f * max_val) : max_val; F32 new_val = kd->value_at_start + (dx / sensitivity) * range; - F32 lo = is_signed ? -max_val : 0.0f; + F32 lo = is_signed ? -max_val : 0.0f; if (new_val < lo) new_val = lo; if (new_val > max_val) new_val = max_val; - if (new_val != *value) { *value = new_val; changed = 1; } + if (new_val != *value) { + *value = new_val; + changed = 1; + } normalized = value_normalize(*value, max_val, is_signed); } @@ -1506,98 +1531,94 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 } // Format value text - S32 val_len = 0; + S32 val_len = 0; char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); // Dimmed accent for fill bar Clay_Color fill_color = g_theme.accent; - fill_color.a = 160; + fill_color.a = 160; // Layout: vertical column (label → hit area with track → value/edit) CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = WIDGET_KNOB_LABEL_GAP, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = WIDGET_KNOB_LABEL_GAP, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Label CLAY_TEXT(clay_str(label), &g_widget_text_config_dim); // Hit area (transparent, sized to encompass thumb travel) Clay_ElementId hit_eid = CLAY_IDI("SlHHit", (S32)hash); - B32 hovered = Clay_PointerOver(hit_eid); + B32 hovered = Clay_PointerOver(hit_eid); CLAY(hit_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(thumb_h) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(thumb_h) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + }) { // Visible track (centered inside hit area) CLAY(CLAY_IDI("SlHTrack", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f)) { // Fill bar F32 fill_w = normalized * track_w; if (fill_w < 1.0f) fill_w = 1.0f; CLAY(CLAY_IDI("SlHFill", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(fill_w), .height = CLAY_SIZING_GROW() }, - }, - .backgroundColor = fill_color, - .cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f) - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(fill_w), .height = CLAY_SIZING_GROW() }, + }, + .backgroundColor = fill_color, .cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f)) {} } // Floating icon thumb (attached to hit area) if (g_icon_pool_count < UI_MAX_ICONS_PER_FRAME) { CustomIconData *idata = &g_icon_pool[g_icon_pool_count++]; - idata->type = CUSTOM_RENDER_ICON; - idata->icon_id = (S32)UI_ICON_SLIDER_THUMB; - idata->color = g_theme.accent; + idata->type = CUSTOM_RENDER_ICON; + idata->icon_id = (S32)UI_ICON_SLIDER_THUMB; + idata->color = g_theme.accent; CLAY(CLAY_IDI("SlHThumb", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, - }, - .floating = { - .offset = { - .x = normalized * (track_w - thumb_w), - .y = 0, - }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - .custom = { .customData = idata }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, + }, + .floating = { + .offset = { + .x = normalized * (track_w - thumb_w), + .y = 0, + }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, + .custom = { .customData = idata }, ) {} } } // Click: F64-click resets, single click starts drag if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) { - B32 is_double_click = (kd->last_click_id == hash && + B32 is_double_click = (kd->last_click_id == hash && (g_frame_number - kd->last_click_frame) < 20); - kd->last_click_id = hash; + kd->last_click_id = hash; kd->last_click_frame = g_frame_number; if (is_double_click) { - if (*value != default_val) { *value = default_val; changed = 1; } - normalized = value_normalize(*value, max_val, is_signed); + if (*value != default_val) { + *value = default_val; + changed = 1; + } + normalized = value_normalize(*value, max_val, is_signed); kd->last_click_id = 0; } else { - kd->dragging_id = hash; - kd->drag_start_x = g_wstate.input.mouse_pos.x; + kd->dragging_id = hash; + kd->drag_start_x = g_wstate.input.mouse_pos.x; kd->value_at_start = *value; } } @@ -1607,22 +1628,21 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 value_edit_render(hash, track_w); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); } else if (val_text && val_len > 0) { - Clay_ElementId val_eid = CLAY_IDI("SlHVal", (S32)hash); - B32 val_hovered = Clay_PointerOver(val_eid); + Clay_ElementId val_eid = CLAY_IDI("SlHVal", (S32)hash); + B32 val_hovered = Clay_PointerOver(val_eid); - Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; + Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; static Clay_TextElementConfig sl_val_cfg = {}; - sl_val_cfg.textColor = g_theme.text; - sl_val_cfg.fontSize = FONT_SIZE_SMALL; - sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + sl_val_cfg.textColor = g_theme.text; + sl_val_cfg.fontSize = FONT_SIZE_SMALL; + sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY(val_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIT() }, - .padding = { uip(2), uip(2), uip(1), uip(1) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIT() }, + .padding = { uip(2), uip(2), uip(1), uip(1) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { CLAY_TEXT(val_str, &sl_val_cfg); } @@ -1641,11 +1661,11 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable) { ensure_widget_text_configs(); - B32 changed = 0; - F32 normalized = value_normalize(*value, max_val, is_signed); - U32 hash = Clay__HashString(clay_str(id), 0).id; - B32 is_editing = (editable && g_wstate.knob_edit_id == hash); - UI_KnobDragState *kd = &g_wstate.knob_drag; + B32 changed = 0; + F32 normalized = value_normalize(*value, max_val, is_signed); + U32 hash = Clay__HashString(clay_str(id), 0).id; + B32 is_editing = (editable && g_wstate.knob_edit_id == hash); + UI_KnobDragState *kd = &g_wstate.knob_drag; F32 track_w = WIDGET_SLIDER_V_TRACK_W; F32 track_h = WIDGET_SLIDER_V_HEIGHT; @@ -1656,19 +1676,22 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 if (!is_editing && kd->dragging_id == hash && g_wstate.input.mouse_down) { B32 shift_now = g_wstate.input.shift_held; if (shift_now != kd->was_shift) { - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; - kd->was_shift = shift_now; + kd->was_shift = shift_now; } - F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; // up = positive + F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; // up = positive F32 sensitivity = 200.0f * g_ui_scale; if (shift_now) sensitivity *= 5.0f; - F32 range = is_signed ? (2.0f * max_val) : max_val; + F32 range = is_signed ? (2.0f * max_val) : max_val; F32 new_val = kd->value_at_start + (dy / sensitivity) * range; - F32 lo = is_signed ? -max_val : 0.0f; + F32 lo = is_signed ? -max_val : 0.0f; if (new_val < lo) new_val = lo; if (new_val > max_val) new_val = max_val; - if (new_val != *value) { *value = new_val; changed = 1; } + if (new_val != *value) { + *value = new_val; + changed = 1; + } normalized = value_normalize(*value, max_val, is_signed); } @@ -1682,99 +1705,95 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 } // Format value text - S32 val_len = 0; + S32 val_len = 0; char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); // Dimmed accent for fill bar Clay_Color fill_color = g_theme.accent; - fill_color.a = 160; + fill_color.a = 160; // Layout: vertical column (label → hit area with track → value/edit) CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = WIDGET_KNOB_LABEL_GAP, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = WIDGET_KNOB_LABEL_GAP, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Label CLAY_TEXT(clay_str(label), &g_widget_text_config_dim); // Hit area (transparent, sized to encompass thumb travel) Clay_ElementId hit_eid = CLAY_IDI("SlVHit", (S32)hash); - B32 hovered = Clay_PointerOver(hit_eid); + B32 hovered = Clay_PointerOver(hit_eid); CLAY(hit_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(track_h) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(track_h) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { // Visible track (centered inside hit area) CLAY(CLAY_IDI("SlVTrack", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, - .childAlignment = { .y = CLAY_ALIGN_Y_BOTTOM }, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f) - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, + .childAlignment = { .y = CLAY_ALIGN_Y_BOTTOM }, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)) { // Fill bar (from bottom) F32 fill_h = normalized * track_h; if (fill_h < 1.0f) fill_h = 1.0f; CLAY(CLAY_IDI("SlVFill", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) }, - }, - .backgroundColor = fill_color, - .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f) - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) }, + }, + .backgroundColor = fill_color, .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)) {} } // Floating icon thumb (attached to hit area) if (g_icon_pool_count < UI_MAX_ICONS_PER_FRAME) { CustomIconData *idata = &g_icon_pool[g_icon_pool_count++]; - idata->type = CUSTOM_RENDER_ICON; - idata->icon_id = (S32)UI_ICON_SLIDER_THUMB; - idata->color = g_theme.accent; + idata->type = CUSTOM_RENDER_ICON; + idata->icon_id = (S32)UI_ICON_SLIDER_THUMB; + idata->color = g_theme.accent; CLAY(CLAY_IDI("SlVThumb", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, - }, - .floating = { - .offset = { - .x = 0, - .y = (1.0f - normalized) * (track_h - thumb_h), - }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - .custom = { .customData = idata }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, + }, + .floating = { + .offset = { + .x = 0, + .y = (1.0f - normalized) * (track_h - thumb_h), + }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, + .custom = { .customData = idata }, ) {} } } // Click: F64-click resets, single click starts drag if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) { - B32 is_double_click = (kd->last_click_id == hash && + B32 is_double_click = (kd->last_click_id == hash && (g_frame_number - kd->last_click_frame) < 20); - kd->last_click_id = hash; + kd->last_click_id = hash; kd->last_click_frame = g_frame_number; if (is_double_click) { - if (*value != default_val) { *value = default_val; changed = 1; } - normalized = value_normalize(*value, max_val, is_signed); + if (*value != default_val) { + *value = default_val; + changed = 1; + } + normalized = value_normalize(*value, max_val, is_signed); kd->last_click_id = 0; } else { - kd->dragging_id = hash; - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->dragging_id = hash; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; } } @@ -1784,22 +1803,21 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 value_edit_render(hash, thumb_w * 2.0f); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); } else if (val_text && val_len > 0) { - Clay_ElementId val_eid = CLAY_IDI("SlVVal", (S32)hash); - B32 val_hovered = Clay_PointerOver(val_eid); + Clay_ElementId val_eid = CLAY_IDI("SlVVal", (S32)hash); + B32 val_hovered = Clay_PointerOver(val_eid); - Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; + Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; static Clay_TextElementConfig sl_val_cfg = {}; - sl_val_cfg.textColor = g_theme.text; - sl_val_cfg.fontSize = FONT_SIZE_SMALL; - sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + sl_val_cfg.textColor = g_theme.text; + sl_val_cfg.fontSize = FONT_SIZE_SMALL; + sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY(val_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .padding = { uip(2), uip(2), uip(1), uip(1) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .padding = { uip(2), uip(2), uip(1), uip(1) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { CLAY_TEXT(val_str, &sl_val_cfg); } @@ -1818,34 +1836,37 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable) { ensure_widget_text_configs(); - B32 changed = 0; - F32 normalized = value_normalize(*value, max_val, is_signed); - U32 hash = Clay__HashString(clay_str(id), 0).id; - B32 is_editing = (editable && g_wstate.knob_edit_id == hash); - UI_KnobDragState *kd = &g_wstate.knob_drag; + B32 changed = 0; + F32 normalized = value_normalize(*value, max_val, is_signed); + U32 hash = Clay__HashString(clay_str(id), 0).id; + B32 is_editing = (editable && g_wstate.knob_edit_id == hash); + UI_KnobDragState *kd = &g_wstate.knob_drag; F32 track_w = WIDGET_FADER_TRACK_W; F32 track_h = WIDGET_FADER_HEIGHT; - F32 cap_w = WIDGET_FADER_CAP_W; - F32 cap_h = WIDGET_FADER_CAP_H; + F32 cap_w = WIDGET_FADER_CAP_W; + F32 cap_h = WIDGET_FADER_CAP_H; // Drag interaction (vertical mouse delta, up = increase) if (!is_editing && kd->dragging_id == hash && g_wstate.input.mouse_down) { B32 shift_now = g_wstate.input.shift_held; if (shift_now != kd->was_shift) { - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; - kd->was_shift = shift_now; + kd->was_shift = shift_now; } - F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; + F32 dy = kd->drag_start_y - g_wstate.input.mouse_pos.y; F32 sensitivity = 200.0f * g_ui_scale; if (shift_now) sensitivity *= 5.0f; - F32 range = is_signed ? (2.0f * max_val) : max_val; + F32 range = is_signed ? (2.0f * max_val) : max_val; F32 new_val = kd->value_at_start + (dy / sensitivity) * range; - F32 lo = is_signed ? -max_val : 0.0f; + F32 lo = is_signed ? -max_val : 0.0f; if (new_val < lo) new_val = lo; if (new_val > max_val) new_val = max_val; - if (new_val != *value) { *value = new_val; changed = 1; } + if (new_val != *value) { + *value = new_val; + changed = 1; + } normalized = value_normalize(*value, max_val, is_signed); } @@ -1859,160 +1880,151 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_ } // Format value text - S32 val_len = 0; + S32 val_len = 0; char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); // Tick mark dimensions F32 tick_major_w = WIDGET_FADER_TICK_MAJOR_W; F32 tick_minor_w = WIDGET_FADER_TICK_MINOR_W; - F32 tick_h = WIDGET_FADER_TICK_H; - S32 num_ticks = 10; - F32 track_left = (cap_w - track_w) / 2.0f; + F32 tick_h = WIDGET_FADER_TICK_H; + S32 num_ticks = 10; + F32 track_left = (cap_w - track_w) / 2.0f; // Layout: vertical column (label → hit area with track → value/edit) CLAY(WID(id), - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .childGap = WIDGET_KNOB_LABEL_GAP, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .childGap = WIDGET_KNOB_LABEL_GAP, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }) { // Label CLAY_TEXT(clay_str(label), &g_widget_text_config_dim); // Hit area (transparent, sized to encompass fader cap travel) Clay_ElementId hit_eid = CLAY_IDI("FdrHit", (S32)hash); - B32 hovered = Clay_PointerOver(hit_eid); + B32 hovered = Clay_PointerOver(hit_eid); CLAY(hit_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(track_h) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(track_h) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { // Visible track (centered inside hit area, empty) CLAY(CLAY_IDI("FdrTrack", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, - }, - .backgroundColor = g_theme.bg_dark, - .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f) - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, + }, + .backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)) {} // Tick marks on both sides of the track for (S32 i = 0; i <= num_ticks; i++) { - F32 norm = (F32)i / (F32)num_ticks; + F32 norm = (F32)i / (F32)num_ticks; F32 tick_y = (1.0f - norm) * (track_h - tick_h); - F32 tw = (i % 5 == 0) ? tick_major_w : tick_minor_w; + F32 tw = (i % 5 == 0) ? tick_major_w : tick_minor_w; // Left tick CLAY(CLAY_IDI("FdrTkL", (S32)(hash * 100 + i)), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, - }, - .backgroundColor = g_theme.text_dim, - .floating = { - .offset = { - .x = track_left - tw, - .y = tick_y, - }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, + }, + .backgroundColor = g_theme.text_dim, .floating = { + .offset = { + .x = track_left - tw, + .y = tick_y, + }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, ) {} // Right tick CLAY(CLAY_IDI("FdrTkR", (S32)(hash * 100 + i)), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, - }, - .backgroundColor = g_theme.text_dim, - .floating = { - .offset = { - .x = track_left + track_w, - .y = tick_y, - }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, + }, + .backgroundColor = g_theme.text_dim, .floating = { + .offset = { + .x = track_left + track_w, + .y = tick_y, + }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, ) {} } // Floating fader cap (RGBA icon from asset SVG) if (g_icon_pool_count < UI_MAX_ICONS_PER_FRAME) { CustomIconData *idata = &g_icon_pool[g_icon_pool_count++]; - idata->type = CUSTOM_RENDER_ICON; - idata->icon_id = (S32)UI_ICON_FADER; - idata->color = Clay_Color{255, 255, 255, 255}; + idata->type = CUSTOM_RENDER_ICON; + idata->icon_id = (S32)UI_ICON_FADER; + idata->color = Clay_Color{ 255, 255, 255, 255 }; F32 cap_y = (1.0f - normalized) * (track_h - cap_h); CLAY(CLAY_IDI("FdrCap", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, - }, - .floating = { - .offset = { .x = 0, .y = cap_y }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - .custom = { .customData = idata }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, + }, + .floating = { + .offset = { .x = 0, .y = cap_y }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, + .custom = { .customData = idata }, ) {} // Color tint overlay on top of fader cap Clay_Color tint = g_theme.accent; - tint.a = 80; - F32 cap_corner = cap_w * (3.0f / 62.2f); + tint.a = 80; + F32 cap_corner = cap_w * (3.0f / 62.2f); CLAY(CLAY_IDI("FdrTint", (S32)hash), - .layout = { - .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, - }, - .backgroundColor = tint, - .cornerRadius = CLAY_CORNER_RADIUS(cap_corner), - .floating = { - .offset = { .x = 0, .y = cap_y }, - .attachPoints = { - .element = CLAY_ATTACH_POINT_LEFT_TOP, - .parent = CLAY_ATTACH_POINT_LEFT_TOP, - }, - .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, - .attachTo = CLAY_ATTACH_TO_PARENT, - .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, - }, - ) {} + .layout = { + .sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, + }, + .backgroundColor = tint, .cornerRadius = CLAY_CORNER_RADIUS(cap_corner), .floating = { + .offset = { .x = 0, .y = cap_y }, + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP, + }, + .pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH, + .attachTo = CLAY_ATTACH_TO_PARENT, + .clipTo = CLAY_CLIP_TO_ATTACHED_PARENT, + }, ) {} } } // Click: F64-click resets, single click starts drag if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) { - B32 is_double_click = (kd->last_click_id == hash && + B32 is_double_click = (kd->last_click_id == hash && (g_frame_number - kd->last_click_frame) < 20); - kd->last_click_id = hash; + kd->last_click_id = hash; kd->last_click_frame = g_frame_number; if (is_double_click) { - if (*value != default_val) { *value = default_val; changed = 1; } - normalized = value_normalize(*value, max_val, is_signed); + if (*value != default_val) { + *value = default_val; + changed = 1; + } + normalized = value_normalize(*value, max_val, is_signed); kd->last_click_id = 0; } else { - kd->dragging_id = hash; - kd->drag_start_y = g_wstate.input.mouse_pos.y; + kd->dragging_id = hash; + kd->drag_start_y = g_wstate.input.mouse_pos.y; kd->value_at_start = *value; } } @@ -2022,22 +2034,21 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_ value_edit_render(hash, cap_w * 2.0f); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); } else if (val_text && val_len > 0) { - Clay_ElementId val_eid = CLAY_IDI("FdrVal", (S32)hash); - B32 val_hovered = Clay_PointerOver(val_eid); + Clay_ElementId val_eid = CLAY_IDI("FdrVal", (S32)hash); + B32 val_hovered = Clay_PointerOver(val_eid); - Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; + Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; static Clay_TextElementConfig fdr_val_cfg = {}; - fdr_val_cfg.textColor = g_theme.text; - fdr_val_cfg.fontSize = FONT_SIZE_SMALL; - fdr_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; + fdr_val_cfg.textColor = g_theme.text; + fdr_val_cfg.fontSize = FONT_SIZE_SMALL; + fdr_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY(val_eid, - .layout = { - .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, - .padding = { uip(2), uip(2), uip(1), uip(1) }, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, - } - ) { + .layout = { + .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() }, + .padding = { uip(2), uip(2), uip(1), uip(1) }, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER }, + }) { CLAY_TEXT(val_str, &fdr_val_cfg); } diff --git a/src/ui/ui_widgets.h b/src/ui/ui_widgets.h index 90dca99..bc039bc 100644 --- a/src/ui/ui_widgets.h +++ b/src/ui/ui_widgets.h @@ -6,9 +6,9 @@ // The caller owns all data — the widget layer only stores transient UI state // like which text field is focused or which dropdown is open. +#include "platform/platform.h" #include "ui/ui_core.h" #include "ui/ui_icons.h" -#include "platform/platform.h" //////////////////////////////// // Widget state (global, managed by widget layer) @@ -17,29 +17,29 @@ #define UI_WIDGET_MAX_TEXT_INPUTS 16 struct UI_KnobDragState { - U32 dragging_id; // Hash of the knob being dragged (0 = none) - F32 drag_start_y; // Mouse Y when drag started - F32 drag_start_x; // Mouse X when drag started (for h-slider) - F32 value_at_start; // Value when drag started - B32 was_shift; // Shift state last frame (to re-anchor on change) - U32 last_click_id; // Knob hash of last click (for F64-click detection) - S32 last_click_frame; // Frame number of last click + U32 dragging_id; // Hash of the knob being dragged (0 = none) + F32 drag_start_y; // Mouse Y when drag started + F32 drag_start_x; // Mouse X when drag started (for h-slider) + F32 value_at_start; // Value when drag started + B32 was_shift; // Shift state last frame (to re-anchor on change) + U32 last_click_id; // Knob hash of last click (for F64-click detection) + S32 last_click_frame; // Frame number of last click }; struct UI_WidgetState { // Text input focus - U32 focused_id; // Clay element ID hash of the focused text input (0 = none) - S32 cursor_pos; // Cursor position in focused text input - F32 cursor_blink; // Blink timer (seconds) + U32 focused_id; // Clay element ID hash of the focused text input (0 = none) + S32 cursor_pos; // Cursor position in focused text input + F32 cursor_blink; // Blink timer (seconds) // Text selection (sel_start == sel_end means no selection) - S32 sel_start; // Selection anchor (where selection began) - S32 sel_end; // Selection extent (moves with cursor) + S32 sel_start; // Selection anchor (where selection began) + S32 sel_end; // Selection extent (moves with cursor) // Tab cycling: registered text input IDs in order of declaration - U32 text_input_ids[UI_WIDGET_MAX_TEXT_INPUTS]; - S32 text_input_count; - B32 tab_pressed; // True on the frame Tab was pressed + U32 text_input_ids[UI_WIDGET_MAX_TEXT_INPUTS]; + S32 text_input_count; + B32 tab_pressed; // True on the frame Tab was pressed // Dropdown U32 open_dropdown_id; // Clay element ID hash of the open dropdown (0 = none) @@ -54,11 +54,11 @@ struct UI_WidgetState { UI_KnobDragState knob_drag; // Knob text edit state - U32 knob_edit_id; // Hash of knob in text edit mode (0 = none) - char knob_edit_buf[32]; // Text buffer for numeric entry - S32 knob_edit_cursor; // Cursor position in edit buffer - S32 knob_edit_sel_start; // Selection anchor - S32 knob_edit_sel_end; // Selection extent + U32 knob_edit_id; // Hash of knob in text edit mode (0 = none) + char knob_edit_buf[32]; // Text buffer for numeric entry + S32 knob_edit_cursor; // Cursor position in edit buffer + S32 knob_edit_sel_start; // Selection anchor + S32 knob_edit_sel_end; // Selection extent }; extern UI_WidgetState g_wstate;