Use platform windows for windowing instead of custom draggable window system

This commit is contained in:
2026-03-05 11:44:23 -05:00
parent b75cb920eb
commit 5eaae4deb9
15 changed files with 833 additions and 742 deletions

View File

@@ -12,12 +12,16 @@
#include "ui/ui_core.h"
#include "ui/ui_icons.h"
#include "ui/ui_widgets.h"
#include "ui/ui_popups.h"
#include "ui/ui_piano.h"
// [cpp]
#include "base/base_inc.cpp"
#include "ui/ui_core.cpp"
#include "ui/ui_icons.cpp"
#include "ui/ui_widgets.cpp"
#include "ui/ui_popups.cpp"
#include "ui/ui_piano.cpp"
#ifdef __APPLE__
#include "platform/platform_macos.mm"
#include "renderer/renderer_metal.mm"
@@ -83,7 +87,7 @@ struct AppState {
S32 bottom_panel_tab; // 0 = Item Editor, 1 = Sample Mapper
// Piano state
S32 piano_mouse_note; // MIDI note held by mouse click (-1 = none)
UI_PianoState piano_state;
// Demo widget state
B32 demo_checkbox_a;
@@ -159,68 +163,6 @@ struct AppState {
};
////////////////////////////////
// Piano helpers
#define PIANO_FIRST_NOTE 21 // A0
#define PIANO_LAST_NOTE 108 // C8
#define PIANO_BLACK_W 11.0f
#define PIANO_BLACK_H_PCT 0.6f
static B32 piano_is_black_key(S32 note) {
S32 n = note % 12;
return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;
}
// Velocity-based color: blue (vel 0) → green (mid) → red (vel 127)
static Clay_Color velocity_color(S32 velocity) {
F32 t = (F32)velocity / 127.0f;
F32 r, g, b;
if (t < 0.5f) {
F32 s = t * 2.0f;
r = 40.0f + s * (76.0f - 40.0f);
g = 120.0f + s * (175.0f - 120.0f);
b = 220.0f + s * (80.0f - 220.0f);
} else {
F32 s = (t - 0.5f) * 2.0f;
r = 76.0f + s * (220.0f - 76.0f);
g = 175.0f + s * (50.0f - 175.0f);
b = 80.0f + s * (40.0f - 80.0f);
}
return Clay_Color{r, g, b, 255};
}
// Piano input: handle mouse clicks on piano keys
static void update_piano_input(AppState *app) {
if (app->master_layout != 0) return;
PlatformInput input = g_wstate.input;
if (!input.mouse_down) {
app->piano_mouse_note = -1;
return;
}
// Find hovered piano key — check black keys first (they're on top)
S32 hovered_note = -1;
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (piano_is_black_key(note) && Clay_PointerOver(CLAY_IDI("PKey", note))) {
hovered_note = note;
break;
}
}
if (hovered_note == -1) {
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (!piano_is_black_key(note) && Clay_PointerOver(CLAY_IDI("PKey", note))) {
hovered_note = note;
break;
}
}
}
if (hovered_note != -1) {
app->piano_mouse_note = hovered_note;
}
}
////////////////////////////////
// Panel builders
@@ -654,7 +596,7 @@ static void build_right_panel(AppState *app) {
// Idle = dark gray
Clay_Color box_color;
if (dev->active) {
box_color = velocity_color(dev->velocity);
box_color = piano_velocity_color(dev->velocity);
} else if (dev->releasing) {
box_color = Clay_Color{255, 255, 255, 255};
} else {
@@ -791,83 +733,9 @@ static void build_log_panel(AppState *app) {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
}
) {
Clay_ElementId piano_id = CLAY_ID("PianoContainer");
CLAY(piano_id,
.layout = {
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
.layoutDirection = CLAY_LEFT_TO_RIGHT,
}
) {
// Compute black key size proportional to white keys
F32 piano_avail_h = uis(app->log_height) - TAB_HEIGHT - uip(8);
F32 black_key_h = piano_avail_h * PIANO_BLACK_H_PCT;
if (black_key_h < uis(20)) black_key_h = uis(20);
F32 white_key_w = ((F32)app->last_w - uip(8)) / 52.0f;
F32 black_key_w = white_key_w * 0.6f;
if (black_key_w < uis(8)) black_key_w = uis(8);
// White keys (grow to fill width and height)
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (piano_is_black_key(note)) continue;
B32 midi_held = midi_is_note_held(app->midi, note);
B32 mouse_held = app->piano_mouse_note == note;
Clay_Color bg;
if (midi_held) {
bg = velocity_color(midi_get_note_velocity(app->midi, note));
} else if (mouse_held) {
bg = g_theme.accent;
} else {
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 } },
) {}
}
// Black keys (floating, attached to left white key)
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (!piano_is_black_key(note)) continue;
Clay_ElementId parent_wkey = CLAY_IDI("PKey", note - 1);
B32 midi_held = midi_is_note_held(app->midi, note);
B32 mouse_held = app->piano_mouse_note == note;
Clay_Color bg;
if (midi_held) {
bg = velocity_color(midi_get_note_velocity(app->midi, note));
} else if (mouse_held) {
bg = g_theme.accent;
} else {
bg = Clay_Color{25, 25, 30, 255};
}
CLAY(CLAY_IDI("PKey", note),
.layout = {
.sizing = {
.width = CLAY_SIZING_FIXED(black_key_w),
.height = CLAY_SIZING_FIXED(black_key_h),
},
},
.backgroundColor = bg,
.cornerRadius = { .topLeft = 0, .topRight = 0, .bottomLeft = uis(2), .bottomRight = uis(2) },
.floating = {
.parentId = parent_wkey.id,
.zIndex = 100,
.attachPoints = {
.element = CLAY_ATTACH_POINT_CENTER_TOP,
.parent = CLAY_ATTACH_POINT_RIGHT_TOP,
},
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
},
) {}
}
}
F32 piano_avail_h = uis(app->log_height) - TAB_HEIGHT - uip(8);
F32 piano_avail_w = (F32)app->last_w - uip(8);
ui_piano(&app->piano_state, app->midi, piano_avail_w, piano_avail_h);
}
}
}
@@ -1882,25 +1750,6 @@ static void build_ui(AppState *app) {
}
}
// Draggable windows (rendered as floating elements above normal UI)
ui_window("WinSettings", "Preferences", &app->show_settings_window,
Vec2F32{100, 100}, Vec2F32{480, 0},
settings_window_content, app);
ui_window("WinAbout", "About", &app->show_about_window,
Vec2F32{200, 150}, Vec2F32{260, 0},
about_window_content, nullptr);
// Modal confirmation dialog
if (app->show_confirm_dialog) {
static const char *confirm_buttons[] = { "Cancel", "OK" };
S32 modal_result = ui_modal("ModalConfirm", "Confirm Action",
"Are you sure you want to proceed? This action cannot be undone.",
confirm_buttons, 2);
if (modal_result != -1) {
app->show_confirm_dialog = 0;
}
}
}
////////////////////////////////
@@ -1990,7 +1839,7 @@ static void do_frame(AppState *app) {
}
ui_widgets_begin_frame(input);
update_piano_input(app);
if (app->master_layout == 0) ui_piano_update_input(&app->piano_state);
update_panel_splitters(app);
// Build UI with Clay
@@ -2071,7 +1920,7 @@ int main(int argc, char **argv) {
app.show_log = 1;
app.show_midi_devices = 1;
app.bottom_panel_tab = 0;
app.piano_mouse_note = -1;
app.piano_state.mouse_note = -1;
app.demo_knob_unsigned = 75.0f;
app.demo_knob_signed = 0.0f;
app.demo_slider_h = 50.0f;
@@ -2116,9 +1965,35 @@ int main(int argc, char **argv) {
default: break;
}
// Native confirmation dialog (blocking)
if (app.show_confirm_dialog) {
S32 result = platform_message_box(window, "Confirm Action",
"Are you sure you want to proceed? This action cannot be undone.",
PLATFORM_MSGBOX_OK_CANCEL);
app.show_confirm_dialog = 0;
(void)result; // result: 0 = OK, 1 = Cancel
}
// Open popup windows when flags transition to 1
if (app.show_settings_window && !popup_find_by_flag(&app.show_settings_window))
popup_open(window, renderer, "Preferences", &app.show_settings_window, 480, 400, settings_window_content, &app);
if (app.show_about_window && !popup_find_by_flag(&app.show_about_window))
popup_open(window, renderer, "About", &app.show_about_window, 260, 200, about_window_content, nullptr);
// Check for OS close on popups
popup_close_check();
if (running) do_frame(&app);
// Render popup windows
// Compute dt for popups (same as main frame)
F32 popup_dt = 1.0f / 60.0f; // approximate; popups don't need precise timing
for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) {
if (g_popups[i].alive) popup_do_frame(&g_popups[i], popup_dt);
}
}
popup_close_all();
audio_destroy(audio);
midi_destroy(midi);
ui_destroy(ui);