chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user