# shaders_lab — proximos pasos: ventana sin decoraciones del SO + botones min/max/close en la MainMenuBar ## Motivacion Hoy la ventana lleva la titlebar nativa de Windows/Linux ademas de la MainMenuBar de ImGui (View). Son dos barras consumiendo ~60 px verticales. Queremos: 1. **Recuperar ese espacio** quitando la titlebar del SO. 2. **Integrar los botones min / max / close** en la MainMenuBar de ImGui (la que renderiza `panel_menu_cpp_core`), alineados a la derecha. 3. Resultado: una sola barra superior compacta con menus + botones de ventana, igual que VSCode/Spotify/etc. Aplicable a **todas las apps** del registry, no solo shaders_lab — debe materializarse como funcion(es) reusables en `cpp/functions/core/`. --- ## Que hace falta ### 1. Crear la ventana sin decoraciones Una linea en `cpp/framework/app_base.cpp` (donde se crea la GLFW window, linea ~37): ```cpp glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); ``` Detras de un nuevo flag `AppConfig::borderless = false` para que las apps existentes sigan iguales por defecto. ### 2. Dibujar la titlebar custom Hoy `panel_menu_cpp_core` ya pinta una `BeginMainMenuBar()` con el menu "View". Ampliamos esa misma barra con: - Titulo de la app a la izquierda (antes del primer menu) — opcional. - Espacio para drag en el centro (la propia barra ya es draggeable porque ImGui detecta clicks fuera de los items). - Tres botones a la derecha alineados con `SameLine` + offset: min (—), max (□ / ⧉ segun estado), close (✕). Glifos: ImGui::ImDrawList con lineas/rect, sin necesidad de fuente de iconos. O symbols Unicode si la fuente los soporta. ### 3. Lo que el SO te daba gratis y hay que reimplementar **Drag de la ventana** ```cpp // Detectar mouse-down en la titlebar (no sobre items): if (!ImGui::IsAnyItemHovered() && ImGui::IsMouseDragging(0)) { double cx, cy; glfwGetCursorPos(window, &cx, &cy); int wx, wy; glfwGetWindowPos(window, &wx, &wy); // guardar offset al iniciar drag, aplicar glfwSetWindowPos cada frame } ``` **Doble-click en titlebar → toggle maximize** ```cpp if (ImGui::IsMouseDoubleClicked(0) && !ImGui::IsAnyItemHovered()) { if (glfwGetWindowAttrib(window, GLFW_MAXIMIZED)) glfwRestoreWindow(window); else glfwMaximizeWindow(window); } ``` **Botones** ```cpp glfwIconifyWindow(window); // min glfwMaximizeWindow(window) / glfwRestoreWindow(window); // max toggle glfwSetWindowShouldClose(window, true); // close ``` **Resize desde bordes** — la parte fea. Sin decoraciones GLFW no expone hit-test de bordes. Hay que detectar mouse en franjas de ~6 px en los 8 lados, cambiar cursor (`glfwSetCursor` con `GLFW_HRESIZE_CURSOR` etc.), y arrastrar reposicionando+resizing manualmente. --- ## Lo que se pierde | Comportamiento | Recuperable | |---------------------------------------------------------|----------------------------------------------------------------------------| | Snap zones de Windows (Win+flecha, drag al borde) | Solo con codigo nativo Win32 (`WM_NCHITTEST`) — GLFW no lo expone | | Aero shake, sombras nativas, animacion de minimizar | No facilmente | | Snap del WM en Linux (i3/sway/KDE) | Igual | | Bugs Wayland posicionando ventanas borderless | Si Linux es WSLg/X11 sin problema; en Wayland nativo verificar primero | | Multi-viewport ImGui (`ConfigFlags_ViewportsEnable`) | Cada ventana secundaria tambien sin titlebar → custom en todas. Mas curro | | Touch / accesibilidad (lectores de pantalla) | Marginal para nuestro caso | --- ## Plan de implementacion ### Fase 1 — funcion reusable + flag en app_base **Funcion nueva**: `custom_titlebar_cpp_core` (componente, pure desde el punto de vista del registry — solo dibuja UI; los efectos GLFW se aplican fuera o se pasan como callbacks). Idealmente fusionada con `panel_menu_cpp_core` o coordinada con ella para que el menu y los botones vivan en la **misma** MainMenuBar. Opcion A (mejor): extender `panel_menu` con parametros opcionales para los botones del SO: ```cpp struct WindowControls { GLFWwindow* window; bool show_min = true; bool show_max = true; bool show_close = true; }; bool panel_menu(const char* menu_label, const PanelToggle* items, std::size_t count, const WindowControls* controls = nullptr); // opcional ``` Pero esto crea acople de `core` con GLFW. Mejor opcion B: Opcion B (limpia): funcion separada `custom_titlebar_cpp_core` que se llama **dentro** del menu existente (despues de los menus, antes del EndMainMenuBar) usando `ImGui::SameLine` con offset al borde derecho. Y una funcion auxiliar `window_controls_cpp_core` para los tres botones, que recibe callbacks (`on_min`, `on_max`, `on_close`) sin saber nada de GLFW. La app las cablea. ```cpp namespace fn_ui { struct WindowButtons { bool min_clicked = false; bool max_clicked = false; bool close_clicked = false; bool is_maximized = false; // input: pinta el icono correcto }; // Renderiza tres iconos al borde derecho de la barra activa // (BeginMainMenuBar o cualquier otro contenedor horizontal). // Devuelve los flags que clico el usuario. WindowButtons window_controls(bool is_maximized); // Drag handler: llamar cada frame cuando mouse esta sobre la barra. // Devuelve delta a aplicar a la posicion de ventana. // (signature por afinar) struct WindowDrag { int dx, dy; bool dragging; }; WindowDrag titlebar_drag_handler(); } // namespace fn_ui ``` La app conecta: ```cpp auto wb = fn_ui::window_controls(glfwGetWindowAttrib(window, GLFW_MAXIMIZED)); if (wb.min_clicked) glfwIconifyWindow(window); if (wb.max_clicked) { /* toggle */ } if (wb.close_clicked) glfwSetWindowShouldClose(window, true); ``` Asi `core` no toca GLFW; `framework/app_base` o cada app cablean lo nativo. **Cambio en app_base**: ```cpp struct AppConfig { // ...existing... bool borderless = false; // true → GLFW_DECORATED=false }; // en run_app(): if (config.borderless) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); ``` ### Fase 2 — integracion en shaders_lab ```cpp fn::AppConfig cfg; cfg.borderless = true; // ... ``` En `render()` despues del `panel_menu("View", ...)`, en la misma barra (reorganizar para que `panel_menu` no cierre `EndMainMenuBar` y deje hacer SameLine al borde derecho con `window_controls`). ### Fase 3 (opcional) — resize por bordes Manejador de hit-test en 8 px alrededor del borde de la ventana. Cambia cursor con `glfwSetCursor`, en mouse-down inicia resize manual con `glfwSetWindowSize` + `glfwSetWindowPos`. ### Fase 4 (solo si se nota la perdida) — snap de Windows nativo `WM_NCHITTEST` via HWND. `#ifdef _WIN32`, `glfwGetWin32Window`, SetWindowLongPtr para subclassear. Trabajo significativo; postponer hasta haber medido si la falta de snap molesta de verdad. --- ## Decisiones pendientes para el dia que se haga 1. Resize manual por bordes en v1 o solo arrastre + maximizar. 2. Si hacemos `WM_NCHITTEST` o aceptamos sin snap de Windows. 3. Multi-viewport ImGui: queda off mientras la titlebar sea custom, o se replica el control en cada secondary window. 4. Forma final del API: `panel_menu` extendido vs `window_controls` aparte (preferencia actual: aparte, mas limpia). --- ## Mi recomendacion practica Empezar minimal: - Borderless ON - Drag arrastrando la MainMenuBar - Doble-click maximiza/restaura - Botones min/max/close al borde derecho - **Sin resize manual** (la ventana es solo maximizable; util para apps tipo lab/dashboard) - **Sin WM_NCHITTEST** (sin snap de Windows) - Multi-viewport off Eso es 80% del valor con 20% del trabajo. Si despues echamos en falta el resize manual o el snap, se anaden incremental.