Format all
This commit is contained in:
31
.clang-format
Normal file
31
.clang-format
Normal file
@@ -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
|
||||
@@ -13,7 +13,7 @@ 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);
|
||||
AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index);
|
||||
|
||||
B32 audio_open_device(AudioEngine *engine, S32 index);
|
||||
void audio_close_device(AudioEngine *engine);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "audio/audio.h"
|
||||
#include <windows.h>
|
||||
#include <math.h>
|
||||
#include <objbase.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define AUDIO_MAX_DEVICES 32
|
||||
#define AUDIO_MAX_CHANNELS 32
|
||||
@@ -124,7 +124,7 @@ enum {
|
||||
// Standard ASIO vtable — inherits IUnknown
|
||||
|
||||
class IASIO : public IUnknown {
|
||||
public:
|
||||
public:
|
||||
virtual ASIOBool init(void *sysHandle) = 0;
|
||||
virtual void getDriverName(char *name) = 0;
|
||||
virtual long getDriverVersion() = 0;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "audio/audio.h"
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
@@ -44,9 +44,11 @@ static OSStatus audio_render_callback(void *inRefCon,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData)
|
||||
{
|
||||
(void)inRefCon; (void)ioActionFlags; (void)inTimeStamp; (void)inBusNumber;
|
||||
AudioBufferList *ioData) {
|
||||
(void)inRefCon;
|
||||
(void)ioActionFlags;
|
||||
(void)inTimeStamp;
|
||||
(void)inBusNumber;
|
||||
|
||||
AudioEngine *engine = g_audio_engine;
|
||||
if (!engine) {
|
||||
@@ -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);
|
||||
|
||||
@@ -34,7 +34,7 @@ void arena_clear(Arena *arena);
|
||||
////////////////////////////////
|
||||
// Temporary scope helpers
|
||||
|
||||
inline Temp temp_begin(Arena *arena) { return {arena, arena->pos}; }
|
||||
inline Temp temp_begin(Arena *arena) { return { arena, arena->pos }; }
|
||||
inline void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.pos); }
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// base_core.h - Fundamental types, macros, and linked list helpers
|
||||
// Inspired by raddebugger's base_core.h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
////////////////////////////////
|
||||
// Codebase keywords
|
||||
@@ -60,13 +60,14 @@ 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 AlignPow2(x, b) (((x) + (b)-1) & (~((b)-1)))
|
||||
#define AlignDownPow2(x, b) ((x) & (~((b)-1)))
|
||||
#define ArrayCount(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
////////////////////////////////
|
||||
@@ -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!")
|
||||
@@ -142,14 +151,9 @@ typedef double F64;
|
||||
|
||||
// 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))))
|
||||
(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)
|
||||
@@ -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)))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -30,51 +30,66 @@ 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 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 };
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -82,20 +97,20 @@ static inline Vec4F32 lerp_4f32(Vec4F32 a, Vec4F32 b, F32 t) {
|
||||
|
||||
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 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
|
||||
|
||||
@@ -12,15 +12,15 @@ 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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
402
src/main.cpp
402
src/main.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
#include "midi/midi.h"
|
||||
#include <CoreMIDI/CoreMIDI.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <string.h>
|
||||
#include <CoreMIDI/CoreMIDI.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MIDI_MAX_DEVICES 64
|
||||
#define MIDI_RELEASE_FLASH_DURATION 0.15f
|
||||
@@ -46,11 +46,14 @@ static void midi_read_callback(const MIDIPacketList *pktlist, void *readProcRefC
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "midi/midi.h"
|
||||
#include <windows.h>
|
||||
#include <mmeapi.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define MIDI_MAX_DEVICES 64
|
||||
#define MIDI_RELEASE_FLASH_DURATION 0.15f
|
||||
|
||||
@@ -4,16 +4,27 @@
|
||||
|
||||
// macOS virtual key codes (avoids Carbon.h include)
|
||||
enum {
|
||||
kVK_ANSI_A = 0x00, kVK_ANSI_C = 0x08, kVK_ANSI_V = 0x09,
|
||||
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_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,
|
||||
};
|
||||
@@ -28,7 +39,7 @@ static U8 macos_keycode_to_pkey(U16 keycode) {
|
||||
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_ForwardDelete: return PKEY_DELETE;
|
||||
case kVK_Escape: return PKEY_ESCAPE;
|
||||
case kVK_LeftArrow: return PKEY_LEFT;
|
||||
case kVK_RightArrow: return PKEY_RIGHT;
|
||||
@@ -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 <NSWindowDelegate> {
|
||||
@public
|
||||
@public
|
||||
PlatformWindow *_platformWindow;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ASmplView : NSView <NSTextInputClient> {
|
||||
@public
|
||||
@public
|
||||
PlatformWindow *_platformWindow;
|
||||
}
|
||||
@end
|
||||
@@ -101,7 +117,10 @@ 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;
|
||||
@@ -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<NSAttributedStringKey> *)validAttributesForMarkedText {
|
||||
return @[];
|
||||
}
|
||||
- (void)unmarkText {}
|
||||
- (NSArray<NSAttributedStringKey> *)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
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
#include <windows.h>
|
||||
|
||||
struct PlatformWindow {
|
||||
HWND hwnd;
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
#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)
|
||||
@@ -172,9 +172,9 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(
|
||||
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,7 +188,7 @@ 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};
|
||||
VkCommandBufferAllocateInfo ai = { 0 };
|
||||
ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
ai.commandPool = r->command_pool;
|
||||
ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
@@ -196,7 +196,7 @@ static VkCommandBuffer begin_one_shot(Renderer *r) {
|
||||
VkCommandBuffer cb;
|
||||
vkAllocateCommandBuffers(r->device, &ai, &cb);
|
||||
|
||||
VkCommandBufferBeginInfo bi = {0};
|
||||
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);
|
||||
@@ -205,7 +205,7 @@ static VkCommandBuffer begin_one_shot(Renderer *r) {
|
||||
|
||||
static void end_one_shot(Renderer *r, VkCommandBuffer cb) {
|
||||
vkEndCommandBuffer(cb);
|
||||
VkSubmitInfo si = {0};
|
||||
VkSubmitInfo si = { 0 };
|
||||
si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
si.commandBufferCount = 1;
|
||||
si.pCommandBuffers = &cb;
|
||||
@@ -220,9 +220,8 @@ static void end_one_shot(Renderer *r, VkCommandBuffer cb) {
|
||||
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};
|
||||
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;
|
||||
@@ -243,7 +242,7 @@ static void transition_image(VkCommandBuffer cb, VkImage image,
|
||||
// Vulkan infrastructure
|
||||
|
||||
static B32 create_instance(Renderer *r) {
|
||||
VkApplicationInfo app_info = {0};
|
||||
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);
|
||||
@@ -265,7 +264,7 @@ static B32 create_instance(Renderer *r) {
|
||||
#endif
|
||||
};
|
||||
|
||||
VkInstanceCreateInfo ci = {0};
|
||||
VkInstanceCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
ci.pApplicationInfo = &app_info;
|
||||
ci.enabledExtensionCount = ArrayCount(extensions);
|
||||
@@ -278,13 +277,10 @@ static B32 create_instance(Renderer *r) {
|
||||
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
VkDebugUtilsMessengerCreateInfoEXT dbg = {0};
|
||||
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.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 =
|
||||
@@ -297,7 +293,7 @@ static B32 create_instance(Renderer *r) {
|
||||
}
|
||||
|
||||
static B32 create_surface(Renderer *r) {
|
||||
VkWin32SurfaceCreateInfoKHR ci = {0};
|
||||
VkWin32SurfaceCreateInfoKHR ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||
ci.hinstance = GetModuleHandle(NULL);
|
||||
ci.hwnd = r->hwnd;
|
||||
@@ -346,7 +342,7 @@ static B32 find_queue_family(Renderer *r) {
|
||||
|
||||
static B32 create_device(Renderer *r) {
|
||||
float priority = 1.0f;
|
||||
VkDeviceQueueCreateInfo queue_ci = {0};
|
||||
VkDeviceQueueCreateInfo queue_ci = { 0 };
|
||||
queue_ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queue_ci.queueFamilyIndex = r->queue_family;
|
||||
queue_ci.queueCount = 1;
|
||||
@@ -354,7 +350,7 @@ static B32 create_device(Renderer *r) {
|
||||
|
||||
const char *extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
|
||||
VkDeviceCreateInfo ci = {0};
|
||||
VkDeviceCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
ci.queueCreateInfoCount = 1;
|
||||
ci.pQueueCreateInfos = &queue_ci;
|
||||
@@ -369,7 +365,7 @@ static B32 create_device(Renderer *r) {
|
||||
}
|
||||
|
||||
static B32 create_vma(Renderer *r) {
|
||||
VmaAllocatorCreateInfo ci = {0};
|
||||
VmaAllocatorCreateInfo ci = { 0 };
|
||||
ci.physicalDevice = r->physical_device;
|
||||
ci.device = r->device;
|
||||
ci.instance = r->instance;
|
||||
@@ -426,7 +422,7 @@ static B32 create_swap_chain(Renderer *r) {
|
||||
if (caps.maxImageCount > 0 && image_count > caps.maxImageCount)
|
||||
image_count = caps.maxImageCount;
|
||||
|
||||
VkSwapchainCreateInfoKHR ci = {0};
|
||||
VkSwapchainCreateInfoKHR ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
ci.surface = r->surface;
|
||||
ci.minImageCount = image_count;
|
||||
@@ -451,7 +447,7 @@ static B32 create_swap_chain(Renderer *r) {
|
||||
|
||||
// Create image views
|
||||
for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
VkImageViewCreateInfo vi = {0};
|
||||
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;
|
||||
@@ -469,7 +465,7 @@ static B32 create_swap_chain(Renderer *r) {
|
||||
}
|
||||
|
||||
static B32 create_render_pass(Renderer *r) {
|
||||
VkAttachmentDescription color_attach = {0};
|
||||
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;
|
||||
@@ -479,16 +475,16 @@ static B32 create_render_pass(Renderer *r) {
|
||||
color_attach.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
color_attach.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkAttachmentReference color_ref = {0};
|
||||
VkAttachmentReference color_ref = { 0 };
|
||||
color_ref.attachment = 0;
|
||||
color_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass = {0};
|
||||
VkSubpassDescription subpass = { 0 };
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &color_ref;
|
||||
|
||||
VkSubpassDependency dep = {0};
|
||||
VkSubpassDependency dep = { 0 };
|
||||
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dep.dstSubpass = 0;
|
||||
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
@@ -496,7 +492,7 @@ static B32 create_render_pass(Renderer *r) {
|
||||
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkRenderPassCreateInfo ci = {0};
|
||||
VkRenderPassCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
ci.attachmentCount = 1;
|
||||
ci.pAttachments = &color_attach;
|
||||
@@ -510,7 +506,7 @@ static B32 create_render_pass(Renderer *r) {
|
||||
|
||||
static B32 create_framebuffers(Renderer *r) {
|
||||
for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
VkFramebufferCreateInfo ci = {0};
|
||||
VkFramebufferCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
ci.renderPass = r->render_pass;
|
||||
ci.attachmentCount = 1;
|
||||
@@ -525,14 +521,14 @@ static B32 create_framebuffers(Renderer *r) {
|
||||
}
|
||||
|
||||
static B32 create_command_resources(Renderer *r) {
|
||||
VkCommandPoolCreateInfo pool_ci = {0};
|
||||
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};
|
||||
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;
|
||||
@@ -545,7 +541,7 @@ 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};
|
||||
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)
|
||||
@@ -553,7 +549,7 @@ static B32 create_command_resources(Renderer *r) {
|
||||
}
|
||||
|
||||
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
|
||||
VkSemaphoreCreateInfo sem_ci = {0};
|
||||
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;
|
||||
@@ -565,7 +561,7 @@ static B32 create_command_resources(Renderer *r) {
|
||||
}
|
||||
|
||||
static B32 create_sampler(Renderer *r) {
|
||||
VkSamplerCreateInfo ci = {0};
|
||||
VkSamplerCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
ci.magFilter = VK_FILTER_LINEAR;
|
||||
ci.minFilter = VK_FILTER_LINEAR;
|
||||
@@ -578,13 +574,13 @@ static B32 create_sampler(Renderer *r) {
|
||||
|
||||
static B32 create_descriptor_resources(Renderer *r) {
|
||||
// Descriptor set layout: single combined image sampler at binding 0
|
||||
VkDescriptorSetLayoutBinding 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;
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo layout_ci = {0};
|
||||
VkDescriptorSetLayoutCreateInfo layout_ci = { 0 };
|
||||
layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
layout_ci.bindingCount = 1;
|
||||
layout_ci.pBindings = &binding;
|
||||
@@ -592,11 +588,11 @@ static B32 create_descriptor_resources(Renderer *r) {
|
||||
return 0;
|
||||
|
||||
// Descriptor pool: 2 sets (font + icon)
|
||||
VkDescriptorPoolSize pool_size = {0};
|
||||
VkDescriptorPoolSize pool_size = { 0 };
|
||||
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
pool_size.descriptorCount = 2;
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_ci = {0};
|
||||
VkDescriptorPoolCreateInfo pool_ci = { 0 };
|
||||
pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_ci.maxSets = 2;
|
||||
pool_ci.poolSizeCount = 1;
|
||||
@@ -605,7 +601,7 @@ static B32 create_descriptor_resources(Renderer *r) {
|
||||
return 0;
|
||||
|
||||
// Allocate font descriptor set
|
||||
VkDescriptorSetAllocateInfo alloc_info = {0};
|
||||
VkDescriptorSetAllocateInfo alloc_info = { 0 };
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
alloc_info.descriptorPool = r->descriptor_pool;
|
||||
alloc_info.descriptorSetCount = 1;
|
||||
@@ -617,7 +613,7 @@ static B32 create_descriptor_resources(Renderer *r) {
|
||||
}
|
||||
|
||||
static VkShaderModule create_shader_module(Renderer *r, const U32 *code, size_t size) {
|
||||
VkShaderModuleCreateInfo ci = {0};
|
||||
VkShaderModuleCreateInfo ci = { 0 };
|
||||
ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
ci.codeSize = size;
|
||||
ci.pCode = code;
|
||||
@@ -631,7 +627,7 @@ 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}};
|
||||
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;
|
||||
@@ -642,57 +638,84 @@ static B32 create_pipeline(Renderer *r) {
|
||||
stages[1].pName = "main";
|
||||
|
||||
// Vertex input
|
||||
VkVertexInputBindingDescription binding = {0};
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
VkPipelineMultisampleStateCreateInfo multisample = { 0 };
|
||||
multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend_attach = {0};
|
||||
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;
|
||||
@@ -700,24 +723,23 @@ static B32 create_pipeline(Renderer *r) {
|
||||
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;
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
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;
|
||||
@@ -729,7 +751,7 @@ static B32 create_pipeline(Renderer *r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_ci = {0};
|
||||
VkGraphicsPipelineCreateInfo pipeline_ci = { 0 };
|
||||
pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_ci.stageCount = 2;
|
||||
pipeline_ci.pStages = stages;
|
||||
@@ -755,12 +777,12 @@ 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};
|
||||
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};
|
||||
VmaAllocationCreateInfo alloc_ci = { 0 };
|
||||
alloc_ci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
@@ -784,12 +806,12 @@ 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};
|
||||
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};
|
||||
VkWriteDescriptorSet write = { 0 };
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = set;
|
||||
write.dstBinding = 0;
|
||||
@@ -862,7 +884,7 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) {
|
||||
}
|
||||
|
||||
// Create Vulkan image
|
||||
VkImageCreateInfo img_ci = {0};
|
||||
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;
|
||||
@@ -875,7 +897,7 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) {
|
||||
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};
|
||||
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) {
|
||||
@@ -884,12 +906,12 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) {
|
||||
}
|
||||
|
||||
// Staging buffer
|
||||
VkBufferCreateInfo staging_ci = {0};
|
||||
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};
|
||||
VmaAllocationCreateInfo staging_alloc_ci = { 0 };
|
||||
staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY;
|
||||
staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
@@ -908,7 +930,7 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) {
|
||||
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;
|
||||
@@ -926,7 +948,7 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) {
|
||||
vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc);
|
||||
|
||||
// Image view
|
||||
VkImageViewCreateInfo view_ci = {0};
|
||||
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;
|
||||
@@ -977,8 +999,7 @@ static void emit_quad(DrawBatch *batch,
|
||||
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 border_thickness, F32 softness, F32 mode) {
|
||||
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
||||
return;
|
||||
|
||||
@@ -988,28 +1009,54 @@ static void emit_quad(DrawBatch *batch,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1019,8 +1066,7 @@ 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 angle_rad) {
|
||||
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
||||
return;
|
||||
|
||||
@@ -1037,34 +1083,49 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1074,8 +1135,7 @@ 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 border_thickness, F32 softness) {
|
||||
emit_quad(batch, x0, y0, x1, y1,
|
||||
0, 0, 0, 0,
|
||||
cr, cg, cb, ca,
|
||||
@@ -1089,8 +1149,7 @@ static void emit_rect_vgradient(DrawBatch *batch,
|
||||
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 softness) {
|
||||
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
||||
return;
|
||||
|
||||
@@ -1100,29 +1159,61 @@ static void emit_rect_vgradient(DrawBatch *batch,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1130,8 +1221,7 @@ static void emit_rect_vgradient(DrawBatch *batch,
|
||||
|
||||
static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
|
||||
Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len,
|
||||
U16 font_size)
|
||||
{
|
||||
U16 font_size) {
|
||||
if (text_len == 0 || color.a < 0.1f) return;
|
||||
|
||||
F32 cr = color.r / 255.f;
|
||||
@@ -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;
|
||||
|
||||
@@ -1406,18 +1495,18 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
VkCommandBuffer cb = fc->command_buffer;
|
||||
vkResetCommandBuffer(cb, 0);
|
||||
|
||||
VkCommandBufferBeginInfo begin_info = {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;
|
||||
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};
|
||||
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];
|
||||
@@ -1428,20 +1517,20 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
|
||||
vkCmdBeginRenderPass(cb, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkViewport viewport = {0};
|
||||
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};
|
||||
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};
|
||||
DrawBatch batch = { 0 };
|
||||
batch.vertices = (UIVertex *)r->vb_mapped[frame_idx];
|
||||
batch.indices = (U32 *)r->ib_mapped[frame_idx];
|
||||
|
||||
@@ -1521,7 +1610,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
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};
|
||||
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);
|
||||
@@ -1534,7 +1623,7 @@ 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};
|
||||
VkRect2D full_scissor = { 0 };
|
||||
full_scissor.extent.width = (U32)r->width;
|
||||
full_scissor.extent.height = (U32)r->height;
|
||||
vkCmdSetScissor(cb, 0, 1, &full_scissor);
|
||||
@@ -1612,7 +1701,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
|
||||
// Submit
|
||||
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkSubmitInfo submit = {0};
|
||||
VkSubmitInfo submit = { 0 };
|
||||
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit.waitSemaphoreCount = 1;
|
||||
submit.pWaitSemaphores = &r->image_available_sema[frame_idx];
|
||||
@@ -1624,7 +1713,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
vkQueueSubmit(r->graphics_queue, 1, &submit, fc->fence);
|
||||
|
||||
// Present
|
||||
VkPresentInfoKHR present = {0};
|
||||
VkPresentInfoKHR present = { 0 };
|
||||
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
present.waitSemaphoreCount = 1;
|
||||
present.pWaitSemaphores = &r->render_finished_sema[frame_idx];
|
||||
@@ -1642,7 +1731,7 @@ 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};
|
||||
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;
|
||||
@@ -1655,18 +1744,18 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
|
||||
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};
|
||||
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};
|
||||
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};
|
||||
VmaAllocationCreateInfo staging_alloc_ci = { 0 };
|
||||
staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY;
|
||||
staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
@@ -1683,7 +1772,7 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
|
||||
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;
|
||||
@@ -1700,7 +1789,7 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
|
||||
vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc);
|
||||
|
||||
// Image view
|
||||
VkImageViewCreateInfo view_ci = {0};
|
||||
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;
|
||||
@@ -1711,7 +1800,7 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
|
||||
vkCreateImageView(r->device, &view_ci, NULL, &r->icon_view);
|
||||
|
||||
// Allocate icon descriptor set
|
||||
VkDescriptorSetAllocateInfo ds_alloc = {0};
|
||||
VkDescriptorSetAllocateInfo ds_alloc = { 0 };
|
||||
ds_alloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
ds_alloc.descriptorPool = r->descriptor_pool;
|
||||
ds_alloc.descriptorSetCount = 1;
|
||||
@@ -1728,8 +1817,14 @@ 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;
|
||||
@@ -1755,8 +1850,14 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,54 +27,54 @@ 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.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.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;
|
||||
}
|
||||
@@ -97,37 +97,37 @@ 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;
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -184,7 +184,7 @@ UI_Context *ui_create(F32 viewport_w, F32 viewport_h) {
|
||||
err_handler.userData = ctx;
|
||||
|
||||
ctx->clay_ctx = Clay_Initialize(clay_arena,
|
||||
Clay_Dimensions{viewport_w, viewport_h},
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Clay_Color piano_velocity_color(S32 velocity) {
|
||||
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) {
|
||||
@@ -32,8 +32,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h)
|
||||
.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);
|
||||
@@ -54,16 +53,14 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h)
|
||||
} 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 } },
|
||||
) {}
|
||||
.backgroundColor = bg, .border = { .color = { 190, 190, 190, 255 }, .width = { .right = 1 } }, ) {}
|
||||
}
|
||||
|
||||
// Black keys (floating, attached to left white key)
|
||||
@@ -79,7 +76,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h)
|
||||
} 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),
|
||||
@@ -89,9 +86,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h)
|
||||
.height = CLAY_SIZING_FIXED(black_key_h),
|
||||
},
|
||||
},
|
||||
.backgroundColor = bg,
|
||||
.cornerRadius = { .topLeft = 0, .topRight = 0, .bottomLeft = uis(2), .bottomRight = uis(2) },
|
||||
.floating = {
|
||||
.backgroundColor = bg, .cornerRadius = { .topLeft = 0, .topRight = 0, .bottomLeft = uis(2), .bottomRight = uis(2) }, .floating = {
|
||||
.parentId = parent_wkey.id,
|
||||
.zIndex = 100,
|
||||
.attachPoints = {
|
||||
@@ -99,8 +94,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h)
|
||||
.parent = CLAY_ATTACH_POINT_RIGHT_TOP,
|
||||
},
|
||||
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
|
||||
},
|
||||
) {}
|
||||
}, ) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -137,8 +140,7 @@ void popup_do_frame(PopupWindow *popup, F32 dt) {
|
||||
.childGap = uip(8),
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
},
|
||||
.backgroundColor = g_theme.bg_medium,
|
||||
) {
|
||||
.backgroundColor = g_theme.bg_medium, ) {
|
||||
if (popup->content_fn) {
|
||||
popup->content_fn(popup->content_user_data);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#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
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
// AFTER a CLAY() block, use Clay_PointerOver(elementId).
|
||||
|
||||
#include "ui/ui_widgets.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
UI_WidgetState g_wstate = {};
|
||||
|
||||
@@ -78,13 +78,11 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
|
||||
CLAY(shadow_eid,
|
||||
.layout = {
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(bb.width + expand*2),
|
||||
.height = CLAY_SIZING_FIXED(bb.height + expand*2),
|
||||
.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 = {
|
||||
.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 = {
|
||||
@@ -92,19 +90,16 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
|
||||
.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),
|
||||
.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 = {
|
||||
.backgroundColor = { 0, 0, 0, per_layer }, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand), .floating = {
|
||||
.offset = { -expand + ox, -expand + oy },
|
||||
.parentId = parent_id,
|
||||
.zIndex = z,
|
||||
@@ -113,8 +108,7 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
|
||||
.parent = parent_attach,
|
||||
},
|
||||
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
|
||||
}
|
||||
) {}
|
||||
}) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +177,7 @@ static void ensure_widget_text_configs() {
|
||||
|
||||
// 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.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;
|
||||
|
||||
@@ -234,8 +228,7 @@ void ui_icon(UI_IconID icon, F32 size, Clay_Color color) {
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) },
|
||||
},
|
||||
.custom = { .customData = data }
|
||||
) {}
|
||||
.custom = { .customData = data }) {}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -247,8 +240,7 @@ void ui_label(const char *id, const char *text) {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -263,8 +255,8 @@ B32 ui_button(const char *id, const char *text) {
|
||||
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 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,
|
||||
@@ -273,10 +265,7 @@ B32 ui_button(const char *id, const char *text) {
|
||||
.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 },
|
||||
) {
|
||||
.backgroundColor = base, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .custom = { .customData = grad }, ) {
|
||||
CLAY_TEXT(clay_str(text), &g_widget_text_config_btn);
|
||||
}
|
||||
|
||||
@@ -299,8 +288,7 @@ B32 ui_checkbox(const char *id, const char *label, B32 *value) {
|
||||
.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) {
|
||||
@@ -311,10 +299,7 @@ B32 ui_checkbox(const char *id, const char *label, B32 *value) {
|
||||
.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 } }
|
||||
) {
|
||||
.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);
|
||||
}
|
||||
@@ -344,8 +329,7 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte
|
||||
.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);
|
||||
@@ -357,8 +341,7 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte
|
||||
.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) {
|
||||
@@ -369,18 +352,13 @@ B32 ui_radio_group(const char *id, const char **options, S32 count, S32 *selecte
|
||||
.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 } }
|
||||
) {
|
||||
.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))
|
||||
) {}
|
||||
.backgroundColor = g_theme.button_text, .cornerRadius = CLAY_CORNER_RADIUS(uis(4))) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -608,7 +594,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
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--;
|
||||
@@ -619,7 +606,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
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++;
|
||||
@@ -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
|
||||
@@ -721,8 +712,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
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,
|
||||
@@ -732,11 +723,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
.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 } },
|
||||
) {
|
||||
.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);
|
||||
@@ -759,8 +746,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() },
|
||||
},
|
||||
.backgroundColor = g_theme.accent
|
||||
) {
|
||||
.backgroundColor = g_theme.accent) {
|
||||
CLAY_TEXT(clay_str(dbuf_sel), &g_widget_text_config_sel);
|
||||
}
|
||||
}
|
||||
@@ -826,12 +812,15 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
while (lo < hi) {
|
||||
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';
|
||||
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,7 +829,7 @@ 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,
|
||||
@@ -850,16 +839,11 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
.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 } },
|
||||
) {
|
||||
.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() },
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
CLAY_TEXT(clay_str(display_label), &g_widget_text_config);
|
||||
}
|
||||
|
||||
@@ -867,8 +851,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -902,9 +885,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
.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 = {
|
||||
.backgroundColor = g_theme.bg_dark, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), .floating = {
|
||||
.parentId = eid.id,
|
||||
.zIndex = 2000,
|
||||
.attachPoints = {
|
||||
@@ -913,8 +894,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
},
|
||||
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
|
||||
},
|
||||
.border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } },
|
||||
) {
|
||||
.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);
|
||||
@@ -924,11 +904,18 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
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 = {
|
||||
@@ -936,9 +923,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
.padding = { uip(8), uip(8), 0, 0 },
|
||||
.childAlignment = { .y = CLAY_ALIGN_Y_CENTER },
|
||||
},
|
||||
.backgroundColor = item_bg,
|
||||
.cornerRadius = item_radius
|
||||
) {
|
||||
.backgroundColor = item_bg, .cornerRadius = item_radius) {
|
||||
CLAY_TEXT(clay_str(options[i]), item_text);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -985,9 +973,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) {
|
||||
.padding = { 0, 0, 0, 0 },
|
||||
.layoutDirection = CLAY_LEFT_TO_RIGHT,
|
||||
},
|
||||
.backgroundColor = g_theme.bg_medium,
|
||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||
) {
|
||||
.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);
|
||||
@@ -1002,9 +988,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) {
|
||||
.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 },
|
||||
) {
|
||||
.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 {
|
||||
@@ -1015,9 +999,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) {
|
||||
.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 },
|
||||
) {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -1049,10 +1031,14 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
|
||||
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,13 +1054,19 @@ 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';
|
||||
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;
|
||||
@@ -1082,8 +1074,11 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
|
||||
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';
|
||||
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();
|
||||
}
|
||||
@@ -1094,7 +1089,8 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
|
||||
if (clip) {
|
||||
if (KE_HAS_SEL()) elen = ke_delete_sel();
|
||||
S32 clip_len = (S32)strlen(clip);
|
||||
char filtered[32]; S32 flen = 0;
|
||||
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,29 +1111,40 @@ 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();
|
||||
}
|
||||
}
|
||||
@@ -1149,17 +1157,19 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
|
||||
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;
|
||||
@@ -1168,7 +1178,10 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
|
||||
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;
|
||||
@@ -1205,7 +1218,7 @@ static void value_edit_render(U32 hash, F32 width) {
|
||||
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.textColor = Clay_Color{ 255, 255, 255, 255 };
|
||||
edit_sel_cfg.fontSize = FONT_SIZE_SMALL;
|
||||
edit_sel_cfg.wrapMode = CLAY_TEXT_WRAP_NONE;
|
||||
|
||||
@@ -1216,31 +1229,30 @@ static void value_edit_render(U32 hash, F32 width) {
|
||||
.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 } }
|
||||
) {
|
||||
.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
|
||||
) {
|
||||
.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);
|
||||
}
|
||||
@@ -1272,7 +1284,10 @@ static void value_edit_click_away(Clay_ElementId eid, F32 *value, F32 max_val, B
|
||||
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;
|
||||
}
|
||||
@@ -1296,8 +1311,11 @@ static void value_edit_enter(U32 hash, F32 value, B32 is_signed) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1347,7 +1368,10 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
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;
|
||||
}
|
||||
@@ -1373,8 +1397,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
.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);
|
||||
|
||||
@@ -1383,9 +1406,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
.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)
|
||||
) {
|
||||
.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++];
|
||||
@@ -1399,8 +1420,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(icon_size), .height = CLAY_SIZING_FIXED(icon_size) },
|
||||
},
|
||||
.custom = { .customData = rdata }
|
||||
) {}
|
||||
.custom = { .customData = rdata }) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1412,7 +1432,10 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
kd->last_click_frame = g_frame_number;
|
||||
|
||||
if (is_double_click) {
|
||||
if (*value != default_val) { *value = default_val; changed = 1; }
|
||||
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;
|
||||
@@ -1442,8 +1465,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -1492,7 +1514,10 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1520,8 +1545,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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);
|
||||
|
||||
@@ -1533,16 +1557,13 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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)
|
||||
) {
|
||||
.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;
|
||||
@@ -1550,9 +1571,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(fill_w), .height = CLAY_SIZING_GROW() },
|
||||
},
|
||||
.backgroundColor = fill_color,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f)
|
||||
) {}
|
||||
.backgroundColor = fill_color, .cornerRadius = CLAY_CORNER_RADIUS(track_h / 2.0f)) {}
|
||||
}
|
||||
|
||||
// Floating icon thumb (attached to hit area)
|
||||
@@ -1579,8 +1598,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
.custom = { .customData = idata }, ) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1592,7 +1610,10 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
kd->last_click_frame = g_frame_number;
|
||||
|
||||
if (is_double_click) {
|
||||
if (*value != default_val) { *value = default_val; changed = 1; }
|
||||
if (*value != default_val) {
|
||||
*value = default_val;
|
||||
changed = 1;
|
||||
}
|
||||
normalized = value_normalize(*value, max_val, is_signed);
|
||||
kd->last_click_id = 0;
|
||||
} else {
|
||||
@@ -1621,8 +1642,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -1668,7 +1688,10 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1696,8 +1719,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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);
|
||||
|
||||
@@ -1709,17 +1731,14 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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)
|
||||
) {
|
||||
.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;
|
||||
@@ -1727,9 +1746,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) },
|
||||
},
|
||||
.backgroundColor = fill_color,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)
|
||||
) {}
|
||||
.backgroundColor = fill_color, .cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)) {}
|
||||
}
|
||||
|
||||
// Floating icon thumb (attached to hit area)
|
||||
@@ -1756,8 +1773,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
.custom = { .customData = idata }, ) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1769,7 +1785,10 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
kd->last_click_frame = g_frame_number;
|
||||
|
||||
if (is_double_click) {
|
||||
if (*value != default_val) { *value = default_val; changed = 1; }
|
||||
if (*value != default_val) {
|
||||
*value = default_val;
|
||||
changed = 1;
|
||||
}
|
||||
normalized = value_normalize(*value, max_val, is_signed);
|
||||
kd->last_click_id = 0;
|
||||
} else {
|
||||
@@ -1798,8 +1817,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -1845,7 +1863,10 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1876,8 +1897,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.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);
|
||||
|
||||
@@ -1889,16 +1909,13 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.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)
|
||||
) {}
|
||||
.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++) {
|
||||
@@ -1911,8 +1928,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) },
|
||||
},
|
||||
.backgroundColor = g_theme.text_dim,
|
||||
.floating = {
|
||||
.backgroundColor = g_theme.text_dim, .floating = {
|
||||
.offset = {
|
||||
.x = track_left - tw,
|
||||
.y = tick_y,
|
||||
@@ -1924,16 +1940,14 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.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 = {
|
||||
.backgroundColor = g_theme.text_dim, .floating = {
|
||||
.offset = {
|
||||
.x = track_left + track_w,
|
||||
.y = tick_y,
|
||||
@@ -1945,8 +1959,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.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)
|
||||
@@ -1954,7 +1967,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
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->color = Clay_Color{ 255, 255, 255, 255 };
|
||||
|
||||
F32 cap_y = (1.0f - normalized) * (track_h - cap_h);
|
||||
|
||||
@@ -1972,8 +1985,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
.custom = { .customData = idata }, ) {}
|
||||
|
||||
// Color tint overlay on top of fader cap
|
||||
Clay_Color tint = g_theme.accent;
|
||||
@@ -1983,9 +1995,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
|
||||
},
|
||||
.backgroundColor = tint,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(cap_corner),
|
||||
.floating = {
|
||||
.backgroundColor = tint, .cornerRadius = CLAY_CORNER_RADIUS(cap_corner), .floating = {
|
||||
.offset = { .x = 0, .y = cap_y },
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
@@ -1994,8 +2004,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
) {}
|
||||
}, ) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2007,7 +2016,10 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
kd->last_click_frame = g_frame_number;
|
||||
|
||||
if (is_double_click) {
|
||||
if (*value != default_val) { *value = default_val; changed = 1; }
|
||||
if (*value != default_val) {
|
||||
*value = default_val;
|
||||
changed = 1;
|
||||
}
|
||||
normalized = value_normalize(*value, max_val, is_signed);
|
||||
kd->last_click_id = 0;
|
||||
} else {
|
||||
@@ -2036,8 +2048,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user