From ace71a6725ad7477ce0e86c958a966c413a6862f Mon Sep 17 00:00:00 2001 From: Max Amundsen Date: Sun, 22 Feb 2026 18:42:14 -0500 Subject: [PATCH] add global menu --- src/main.cpp | 96 ++++++++++++++++++++++++++++----- src/platform/platform.h | 14 +++++ src/platform/platform_win32.cpp | 42 +++++++++++++++ 3 files changed, 138 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 76f244d..a8d094f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,54 @@ #include "imgui.h" #include "imgui_internal.h" +enum MenuCmd { + MENU_NONE = 0, + MENU_FILE_NEW, + MENU_FILE_OPEN, + MENU_FILE_SAVE, + MENU_FILE_SAVE_AS, + MENU_FILE_EXIT, + MENU_IMPORT_AUDIO, + MENU_IMPORT_MIDI, + MENU_VIEW_BROWSER, + MENU_VIEW_PROPERTIES, + MENU_VIEW_LOG, + MENU_VIEW_DEMO, +}; + +static void setup_menus(PlatformWindow *window) +{ + PlatformMenuItem file_items[] = { + { "New", MENU_FILE_NEW }, + { "Open...", MENU_FILE_OPEN }, + { "Save", MENU_FILE_SAVE }, + { "Save As...", MENU_FILE_SAVE_AS }, + { nullptr, 0 }, + { "Exit", MENU_FILE_EXIT }, + }; + + PlatformMenuItem import_items[] = { + { "Audio...", MENU_IMPORT_AUDIO }, + { "MIDI...", MENU_IMPORT_MIDI }, + }; + + PlatformMenuItem view_items[] = { + { "Browser", MENU_VIEW_BROWSER }, + { "Properties", MENU_VIEW_PROPERTIES }, + { "Log", MENU_VIEW_LOG }, + { nullptr, 0 }, + { "Demo", MENU_VIEW_DEMO }, + }; + + PlatformMenu menus[] = { + { "File", file_items, sizeof(file_items) / sizeof(file_items[0]) }, + { "Import", import_items, sizeof(import_items) / sizeof(import_items[0]) }, + { "View", view_items, sizeof(view_items) / sizeof(view_items[0]) }, + }; + + platform_set_menu(window, menus, sizeof(menus) / sizeof(menus[0])); +} + static void build_default_layout(ImGuiID dockspace_id) { ImGui::DockBuilderRemoveNode(dockspace_id); @@ -45,11 +93,25 @@ int main(int argc, char **argv) return 1; } + setup_menus(window); + int32_t last_w = w, last_h = h; - bool show_demo = true; - bool first_frame = true; + bool show_demo = true; + bool show_browser = true; + bool show_props = true; + bool show_log = true; + bool first_frame = true; while (platform_poll_events(window)) { + int32_t menu_cmd = platform_poll_menu_command(window); + switch (menu_cmd) { + case MENU_FILE_EXIT: platform_destroy_window(window); return 0; + case MENU_VIEW_BROWSER: show_browser = !show_browser; break; + case MENU_VIEW_PROPERTIES:show_props = !show_props; break; + case MENU_VIEW_LOG: show_log = !show_log; break; + case MENU_VIEW_DEMO: show_demo = !show_demo; break; + default: break; + } platform_get_size(window, &w, &h); if (w != last_w || h != last_h) { renderer_resize(renderer, w, h); @@ -70,10 +132,12 @@ int main(int argc, char **argv) } // Left panel - ImGui::Begin("Browser"); - ImGui::Text("Instruments"); - ImGui::Separator(); - ImGui::End(); + if (show_browser) { + ImGui::Begin("Browser", &show_browser); + ImGui::Text("Instruments"); + ImGui::Separator(); + ImGui::End(); + } // Main content ImGui::Begin("Main"); @@ -81,16 +145,20 @@ int main(int argc, char **argv) ImGui::End(); // Right panel - ImGui::Begin("Properties"); - ImGui::Text("Details"); - ImGui::Separator(); - ImGui::End(); + if (show_props) { + ImGui::Begin("Properties", &show_props); + ImGui::Text("Details"); + ImGui::Separator(); + ImGui::End(); + } // Bottom panel - ImGui::Begin("Log"); - ImGui::Text("Output / Log"); - ImGui::Separator(); - ImGui::End(); + if (show_log) { + ImGui::Begin("Log", &show_log); + ImGui::Text("Output / Log"); + ImGui::Separator(); + ImGui::End(); + } // Demo window if (show_demo) diff --git a/src/platform/platform.h b/src/platform/platform.h index 1910b82..3436e44 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -11,8 +11,22 @@ struct PlatformWindowDesc { int32_t height = 720; }; +struct PlatformMenuItem { + const char *label; // nullptr = separator + int32_t id; // command ID (ignored for separators) +}; + +struct PlatformMenu { + const char *label; + PlatformMenuItem *items; + int32_t item_count; +}; + PlatformWindow *platform_create_window(PlatformWindowDesc *desc); void platform_destroy_window(PlatformWindow *window); bool platform_poll_events(PlatformWindow *window); void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h); void *platform_get_native_handle(PlatformWindow *window); + +void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count); +int32_t platform_poll_menu_command(PlatformWindow *window); diff --git a/src/platform/platform_win32.cpp b/src/platform/platform_win32.cpp index eecb080..757e264 100644 --- a/src/platform/platform_win32.cpp +++ b/src/platform/platform_win32.cpp @@ -15,6 +15,7 @@ struct PlatformWindow { bool should_close; int32_t width; int32_t height; + int32_t pending_menu_cmd; }; static PlatformWindow *g_current_window = nullptr; @@ -31,6 +32,10 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM g_current_window->height = (int32_t)HIWORD(lparam); } return 0; + case WM_COMMAND: + if (g_current_window && HIWORD(wparam) == 0) + g_current_window->pending_menu_cmd = (int32_t)LOWORD(wparam); + return 0; case WM_CLOSE: if (g_current_window) g_current_window->should_close = true; @@ -133,3 +138,40 @@ void *platform_get_native_handle(PlatformWindow *window) { return (void *)window->hwnd; } + +void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count) +{ + HMENU menu_bar = CreateMenu(); + + for (int32_t i = 0; i < menu_count; i++) { + HMENU submenu = CreatePopupMenu(); + + for (int32_t j = 0; j < menus[i].item_count; j++) { + PlatformMenuItem *item = &menus[i].items[j]; + if (!item->label) { + AppendMenuW(submenu, MF_SEPARATOR, 0, nullptr); + } else { + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, nullptr, 0); + wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, item->label, -1, wlabel, wchar_count); + AppendMenuW(submenu, MF_STRING, (UINT_PTR)item->id, wlabel); + _freea(wlabel); + } + } + + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, nullptr, 0); + wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, wlabel, wchar_count); + AppendMenuW(menu_bar, MF_POPUP, (UINT_PTR)submenu, wlabel); + _freea(wlabel); + } + + SetMenu(window->hwnd, menu_bar); +} + +int32_t platform_poll_menu_command(PlatformWindow *window) +{ + int32_t cmd = window->pending_menu_cmd; + window->pending_menu_cmd = 0; + return cmd; +}