docs(flows): DoD obligatorio con user-facing surface + abrir issues 0100-0103 (taxonomia, frontmatter migration, dev_console, work dashboard)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "app_base.h"
|
||||
#include "version_generated.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@@ -178,6 +180,50 @@ static void install_sizemove_subclass_hwnd(HWND hwnd) {
|
||||
g_subclassed[hwnd] = orig;
|
||||
}
|
||||
|
||||
// Resource ID generado por cpp/CMakeLists.txt en <target>_appicon.rc:
|
||||
// 101 ICON "<app_dir>/appicon.ico"
|
||||
// Si la app no tiene appicon.ico el .rc no se genera y LoadImageW devuelve
|
||||
// NULL — no error visible, los HWND quedan con el icono GLFW por defecto.
|
||||
#define FN_APP_ICON_RES_ID 101
|
||||
|
||||
// Carga el icono embebido al tamaño OS-recomendado para small (title bar) y
|
||||
// big (Alt+Tab / taskbar). LR_SHARED -> Windows gestiona el handle; no hay
|
||||
// que DestroyIcon. Cacheado por HMODULE+ID+size.
|
||||
static HICON load_app_icon(int cx, int cy) {
|
||||
HMODULE mod = GetModuleHandleW(nullptr);
|
||||
return (HICON)LoadImageW(mod, MAKEINTRESOURCEW(FN_APP_ICON_RES_ID),
|
||||
IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTCOLOR);
|
||||
}
|
||||
|
||||
// Adjunta el icono embebido al HWND:
|
||||
// WM_SETICON ICON_SMALL -> title bar (16x16) y Alt+Tab small variant.
|
||||
// WM_SETICON ICON_BIG -> taskbar (32x32) y Alt+Tab big variant.
|
||||
// SetClassLongPtrW propaga el icono al WNDCLASS para que nuevos HWNDs de la
|
||||
// misma clase lo hereden (no critico — el per-frame scan ya cubre cada
|
||||
// viewport secundario via su HWND propio, que puede tener WNDCLASS distinta).
|
||||
static std::unordered_set<HWND> g_icon_attached;
|
||||
static void attach_app_icon_to_hwnd(HWND hwnd) {
|
||||
if (!hwnd) return;
|
||||
if (g_icon_attached.count(hwnd)) return; // idempotent
|
||||
HICON hSmall = load_app_icon(GetSystemMetrics(SM_CXSMICON),
|
||||
GetSystemMetrics(SM_CYSMICON));
|
||||
HICON hBig = load_app_icon(GetSystemMetrics(SM_CXICON),
|
||||
GetSystemMetrics(SM_CYICON));
|
||||
if (!hSmall && !hBig) return; // no appicon.ico embebido — nada que hacer
|
||||
if (hSmall) SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hSmall);
|
||||
if (hBig) SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hBig);
|
||||
if (hSmall) SetClassLongPtrW(hwnd, GCLP_HICONSM, (LONG_PTR)hSmall);
|
||||
if (hBig) SetClassLongPtrW(hwnd, GCLP_HICON, (LONG_PTR)hBig);
|
||||
g_icon_attached.insert(hwnd);
|
||||
}
|
||||
|
||||
static void prune_dead_icon_attached() {
|
||||
for (auto it = g_icon_attached.begin(); it != g_icon_attached.end();) {
|
||||
if (!IsWindow(*it)) it = g_icon_attached.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
}
|
||||
|
||||
static void install_sizemove_subclass(GLFWwindow* w) {
|
||||
if (!w) return;
|
||||
install_sizemove_subclass_hwnd(glfwGetWin32Window(w));
|
||||
@@ -337,6 +383,14 @@ void migrate_to_local_files(const char* const* names, std::size_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* framework_version() {
|
||||
return FN_MODULE_FRAMEWORK_VERSION;
|
||||
}
|
||||
|
||||
const char* framework_description() {
|
||||
return FN_MODULE_FRAMEWORK_DESCRIPTION;
|
||||
}
|
||||
|
||||
int run_app(AppConfig config, std::function<void()> render_fn) {
|
||||
// Logger primero para capturar fallos del propio init (GLFW, ventana, GL).
|
||||
if (config.log.file_path != nullptr) {
|
||||
@@ -401,6 +455,11 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
|
||||
// thread; we observe them and skip render+swap so the compositor moves
|
||||
// the existing buffer (same contract as native title-bar drag).
|
||||
install_sizemove_subclass(window);
|
||||
|
||||
// Adjuntar appicon embebido al HWND principal para que aparezca en la
|
||||
// barra de tareas, Alt+Tab y title bar (GLFW no propaga el icono de
|
||||
// recursos del .exe a su WNDCLASS por defecto).
|
||||
attach_app_icon_to_hwnd(glfwGetWin32Window(window));
|
||||
#endif
|
||||
|
||||
// Carga punteros a funciones GL >= 2.0 si la app lo pide. En Linux es
|
||||
@@ -565,11 +624,18 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
|
||||
// their very first frame onwards.
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
prune_dead_subclassed();
|
||||
prune_dead_icon_attached();
|
||||
ImGuiPlatformIO& pio_sub = ImGui::GetPlatformIO();
|
||||
for (int i = 0; i < pio_sub.Viewports.Size; ++i) {
|
||||
ImGuiViewport* vp = pio_sub.Viewports[i];
|
||||
if (!vp || !vp->PlatformHandle) continue;
|
||||
install_sizemove_subclass((GLFWwindow*)vp->PlatformHandle);
|
||||
GLFWwindow* gw = (GLFWwindow*)vp->PlatformHandle;
|
||||
install_sizemove_subclass(gw);
|
||||
// Floating panels = secondary HWNDs creados por el backend
|
||||
// GLFW. WNDCLASS distinta de la main -> no heredan icono via
|
||||
// SetClassLongPtrW. WM_SETICON per-HWND es la unica forma de
|
||||
// que el taskbar/titlebar muestren el icono.
|
||||
attach_app_icon_to_hwnd(glfwGetWin32Window(gw));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -82,6 +82,11 @@ const char* asset_path(const char* name);
|
||||
// apps lo llaman al iniciar para migrar instalaciones viejas.
|
||||
void migrate_to_local_files(const char* const* names, std::size_t n);
|
||||
|
||||
// Framework metadata (auto-generated from modules/framework/module.md via
|
||||
// `fn index`). About panel reads these.
|
||||
const char* framework_version();
|
||||
const char* framework_description();
|
||||
|
||||
// Modos de tema para run_app.
|
||||
enum class ThemeMode {
|
||||
FnDark, // Identidad del registry (Mantine v9 dark + indigo). DEFAULT.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Module manifest visible to fn_framework's About panel.
|
||||
//
|
||||
// Each app gets an auto-generated <app>_modules_generated.cpp (codegen via
|
||||
// python/functions/infra/codegen_app_modules.py, invoked by add_imgui_app at
|
||||
// CMake configure time) that defines the array + count below from the app's
|
||||
// `uses_modules:` declaration in its app.md.
|
||||
//
|
||||
// Apps without uses_modules still get a stub array of length 0 — links cleanly.
|
||||
//
|
||||
// Framework reads via:
|
||||
//
|
||||
// for (size_t i = 0; i < fn::app_modules_count; ++i) {
|
||||
// const auto& m = fn::app_modules_array[i];
|
||||
// ImGui::Text("%s v%s — %s", m.name, m.version, m.description);
|
||||
// }
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace fn {
|
||||
|
||||
struct ModuleInfo {
|
||||
const char* name;
|
||||
const char* version;
|
||||
const char* description;
|
||||
};
|
||||
|
||||
extern const ModuleInfo app_modules_array[];
|
||||
extern const unsigned long app_modules_count;
|
||||
|
||||
} // namespace fn
|
||||
Reference in New Issue
Block a user