Mission Vishwakarma Vishwakarma Download the .exe Roadmap Pricing

User Interface

We follow the standard ribbon interface for UI. Further, supporting all indian scheduled languages is must. This will als enable us to support international languages in future. Please refer below actual code files for user interface design specifications. We have baseline strings say N numbers in English. Each string can have a corresponding language translation or if the language translation is empty string, than the corresponding english text shall be followed. All translations are stored in UserInterface-Text.h file.

Supporting all Indian languages is mostly a data organization + text shaping problem, not a rendering problem. Renderer will just draw glyphs; the system around it decides which string to show.

Translation Document

  1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
  2#pragma once
  3
  4enum class UILanguage : uint8_t
  5{
  6    English = 0,
  7
  8    // 22 Indian scheduled languages
  9    /* Here is the list of the given languages arranged in descending order of the number of speakers.
 10    2011 Census of India data for total speakers, including both native/mother tongue and second-language speakers where reported,
 11    as this is the most comprehensive official source available). */
 12
 13    Hindi, // ~528–691 million total speakers; ~43.63% of India's population as native speakers alone)
 14    Bengali, // ~97–107 million
 15    Marathi, // ~83–99 million
 16    Telugu, // ~81–94 million
 17    Tamil, // ~69–76 million
 18    Gujarati, // ~55–60 million
 19    Urdu, // ~50–63 million
 20    Kannada, // ~43–58 million
 21    Odia, // ~37–42 million
 22    Malayalam, // ~34–35 million
 23    Punjabi, // ~33–36 million
 24    Assamese, // ~15–23 million
 25    Maithili, // ~13–14 million, based on ~1.12% share)
 26    Santali, // ~7.3–7.7 million
 27    Kashmiri, // ~6.8–7 million
 28    Nepali, // ~2.9–3 million
 29    Sindhi, // ~2.7–3 million
 30    Dogri, // ~2.6–2.8 million
 31    Konkani, // ~2.2–2.6 million
 32    Manipuri, // (Meitei) ~1.7–2 million
 33    Bodo, // ~1.4–1.6 million
 34    Sanskrit, // ~25,000 native speakers; higher if including those reporting knowledge, but still by far the smallest)
 35
 36    // Major global engineering languages. Population number by Grok citing 
 37    ChineseSimplified, // Both Chinese combined ~1.18–1.20 billion total speakers (mostly native)
 38    ChineseTraditional, //(Mandarin Chinese)
 39    Spanish, // ~558–560 million
 40    Portuguese, // ~264–270 million
 41    Russian, // ~253–260 million
 42    French, // ~312–330 million (some sources place it slightly above or near Arabic depending on L2 counting)
 43    Arabic, // ~335 million (Modern Standard Arabic + varieties; widely used in engineering contexts across the Middle East)
 44    Indonesian, // ~200–255 million
 45    German, // ~130–134 million
 46    Japanese, // ~125–126 million. Covers all of Katakana , Kanji and Hiragana symbols within same fonts.
 47    Vietnamese, // ~85–97 million
 48    Turkish, // ~80–90 million
 49    Persian, // (Farsi) — ~70–82 million. Farsi - Iran engineering market
 50    Korean, // ~80–85 million
 51    Italian, // ~65–90 million
 52    Thai, // ~60–70 million
 53    Polish, // ~45–50 million
 54    Ukrainian, // ~35–45 million
 55    Dutch, // ~25–30 million
 56    Filipino, // (Tagalog) ~80–90 million total (native ~25–30 million + significant L2 in Philippines)
 57    Swedish, // ~10–15 million
 58    Czech, // ~10–12 million
 59    Hungarian, // ~12–14 million
 60
 61    COUNT
 62};
 63
 64/*
 65ChatGPT analysis of population coverage by above 46 languages:
 66
 67| Metric                         | Result     |
 68| ------------------------------ | ---------- |
 69| World population coverage      | **90–94%** |
 70| Engineering workforce coverage | **97–99%** |
 71| India coverage                 | **~99%**   |
 72| Europe coverage                | **~95%**   |
 73| Americas coverage              | **~95%**   |
 74
 75All these 46 languages translate to 13 unique scripts. Unicode handles all of them well.
 76
 77| Script        | Languages                             |
 78| ------------- | ------------------------------------- |
 79| Latin         | English, German, French, Spanish, etc |
 80| Cyrillic      | Russian, Ukrainian, Bulgarian, etc    |
 81| Devanagari    | Hindi, Marathi, Nepali, Sanskrit etc  |
 82| Bengali       | Bengali, Assamese                     |
 83| Gurmukhi      | Punjabi                               |
 84| Gujarati      | Gujarati                              |
 85| Odia          | Odia                                  |
 86| Tamil         | Tamil                                 |
 87| Telugu        | Telugu                                |
 88| Kannada       | Kannada                               |
 89| Malayalam     | Malayalam                             |
 90| Meetei Mayek  | Manipuri (Meitei)                     |
 91| Ol Chiki      | Santali                               |
 92| Arabic script | Urdu, Arabic, Persian, Kashmiri       |
 93| Chinese Han   | Chinese + Japanese Kanji              |
 94| Japanese kana | Hiragana/Katakana                     |
 95| Hangul        | Korean                                |
 96| Thai          | Thai                                  |
 97
 98Professional CAD software language coverage (As per ChatGPT).
 99| Software   | Languages |
100| ---------- | --------- |
101| AutoCAD    | ~15       |
102| SolidWorks | ~13       |
103| Fusion360  | ~10       |
104| CATIA      | ~8        |
105All softwares listed below are copy right of respective software companies.
106
107HENCE OUR LANGUAGE LIST IS FROZEN ! ;)
108
109Estimated size overhead of bundling all the fonts:
110
111| Font                         | Typical Size |
112| ---------------------------- | ------------ |
113| Noto Sans (Latin + extended) | ~2 MB        |
114| Noto Sans Devanagari         | ~1.5 MB      |
115| Noto Sans Bengali            | ~1.3 MB      |
116| Noto Sans Gurmukhi           | ~0.9 MB      |
117| Noto Sans Gujarati           | ~1.0 MB      |
118| Noto Sans Oriya (Odia)       | ~1.1 MB      |
119| Noto Sans Tamil              | ~0.9 MB      |
120| Noto Sans Telugu             | ~1.2 MB      |
121| Noto Sans Kannada            | ~1.2 MB      |
122| Noto Sans Malayalam          | ~1.4 MB      |
123| Noto Sans Arabic             | ~1.2 MB      |
124| Noto Sans Thai               | ~0.7 MB      |
125
126Subtotal (non-CJK): ≈ 14–15 MB
127
128| Font                                   | Approx Size |
129| -------------------------------------- | ----------- |
130| Noto Sans CJK SC (Simplified Chinese)  | ~16–18 MB   |
131| Noto Sans CJK TC (Traditional Chinese) | ~16–18 MB   |
132| Noto Sans CJK JP (Japanese)            | ~16–18 MB   |
133| Noto Sans CJK KR (Korean)              | ~16–18 MB   |
134
135CJK 3 variants (SC + JP + KR): ≈ 48–54 MB
136
137Total: ≈ 65 MB , ~60% Compression expected in Installer. ≈ 40 MB. Acceptable.
138
139Runtime: Entire font files will not be loaded at runtime.
140They will be loaded on demand to minimize memory footprint.
141
142*/

User Interface Design Document and Implementation!

 1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
 2#pragma once
 3
 4#define WIN32_LEAN_AND_MEAN
 5#define NOMINMAX   //
 6#include <windows.h>   // MUST be before d3d12.h
 7#include <d3d12.h>
 8#include <d3dx12.h>
 9#include <dxgi1_6.h>
10#include <wrl.h>
11#include <vector>
12#include <array>
13#include <unordered_map>
14#include <iostream>
15#include <atomic>
16
17#include "ConstantsApplication.h"
18#include "UserInterface.h" // It also includes "UserInterface-TextTranslations.h"
19
20// Do not #include "विश्वकर्मा.h" otherwise it will lead to circular dependency error. Declare this struct exist.
21struct SingleUIWindow; // Add this forward declaration:
22
23using Microsoft::WRL::ComPtr;
24
25struct DX12ResourcesUI { // GPU resources
26    std::array<ComPtr<ID3D12Resource>, UI_MAX_ATLAS_TEXTURES> uiAtlasTextures; // 1024×1024 or 2048×2048 RGBA (or R8 for alpha-only)
27    ComPtr<ID3D12Resource> uiVertexBuffer; // Dynamic upload buffer for vertices
28    ComPtr<ID3D12Resource> uiIndexBuffer;  // Dynamic upload buffer for indices
29
30    UINT8* pVertexDataBegin = nullptr; // Mapped pointer for immediate writing
31    UINT8* pIndexDataBegin = nullptr;
32    UINT8* pOrthoDataBegin = nullptr;
33
34    ComPtr<ID3D12PipelineState> uiPSO;
35    ComPtr<ID3D12RootSignature> uiRootSignature;
36    ComPtr<ID3D12Resource> uiOrthoConstantBuffer;
37    ComPtr<ID3D12DescriptorHeap> srvHeap;
38    ComPtr<ID3D12DescriptorHeap> samplerHeap;
39
40    uint32_t maxVertices = 65536;
41    uint32_t maxIndices = 65536 * 3;
42};
43
44struct UIDrawContext { // Draw context
45    UIVertex* vertexPtr;
46    uint16_t* indexPtr;
47    uint32_t vertexCount, indexCount;
48};
49
50// DirectX12 Immediate Mode UI System (Phase 4A). Tab Bar Rendering Only
51// External interfaces of User Interface sub module of the code.
52void InitUIResources(DX12ResourcesUI& uiRes, ID3D12Device* device);
53void CleanupUIResources(DX12ResourcesUI& uiRes);
54
55void PushRect(UIDrawContext& ctx, float x, float y, float w, float h, uint32_t color, DX12ResourcesUI& uiRes);
56void PushRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
57    uint32_t color, DX12ResourcesUI& uiRes);
58void PushTopRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
59    uint32_t color, DX12ResourcesUI& uiRes);
60void PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes);
61
62// Slots 0 and 1 are currently reserved for the mandatory English and Icon atlases.
63// Future script atlases can use slots [UI_FIRST_DYNAMIC_SCRIPT_ATLAS_SLOT, UI_MAX_ATLAS_TEXTURES).
64bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
65    const AtlasBitmap& atlas);
66
67void PrecomputeTopRibbonLayout(UITopRibbonLayout& layout, float monitorDPIX, float monitorDPIY);
68
69void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmdList,
70    DX12ResourcesUI& uiRes, UITopRibbonLayout& topRibbonLayout,
71    float monitorDPIX, float monitorDPIY, const UIInput& input);
   1// Copyright (c) 2026-Present : Ram Shanker: All rights reserved.
   2
   3#include "UserInterface-DirectX12.h"
   4#include <algorithm>
   5#include <d3dcompiler.h>
   6#include "ShaderUIVertex.h"
   7#include "ShaderUIPixel.h"
   8#include "FontManager.h"
   9#include "..\build\NotoSansMSDF_Compiled.h"
  10#include <MemoryManagerGPU-DirectX12.h>
  11#include "विश्वकर्मा.h"
  12#include "TextureSaver.h"
  13#include "DataTreeView.h"
  14#include "UserInterfaceTranslationCompiled.h"
  15#include <array>
  16#include <cmath>
  17#include <utility>
  18extern शंकर gpu;
  19extern std::atomic<uint16_t*> publishedTabIndexes;
  20extern std::atomic<uint16_t>  publishedTabCount;
  21extern void PrintHResult(int);
  22std::atomic<uint32_t> actionWriteIndex;
  23// ASCII Character set.
  24std::string charset = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
  25std::atomic<uint64_t> atlasFence = 0;
  26
  27struct UIAtlasRegion {
  28    float uvMinX = 0.0f;
  29    float uvMinY = 0.0f;
  30    float uvMaxX = 0.0f;
  31    float uvMaxY = 0.0f;
  32};
  33
  34struct UIRoundedRectangleNineSlice {
  35    std::array<std::array<UIAtlasRegion, 3>, 3> regions{};
  36};
  37
  38struct UIIconAtlasMetadata {
  39    UIRoundedRectangleNineSlice roundedRectangle{};
  40    std::array<char32_t, 4> dummyIconCodepoints{ U'\uE100', U'\uE101', U'\uE102', U'\uE103' };
  41    std::vector<char32_t> mixedIconCodepoints{};
  42};
  43
  44static UIIconAtlasMetadata gIconAtlasMetadata{};
  45
  46static const char* LogicalObjectName(VishwakarmaStorage::ObjectType objectType, const META_DATA* object) {
  47    if (!object) return "";
  48
  49    switch (objectType) {
  50    case VishwakarmaStorage::ObjectType::Folder:
  51        return static_cast<const FOLDER*>(object)->name;
  52    case VishwakarmaStorage::ObjectType::Page2D:
  53        return static_cast<const PAGE2D*>(object)->name;
  54    case VishwakarmaStorage::ObjectType::Scene3D:
  55        return static_cast<const SCENE3D*>(object)->name;
  56    default:
  57        return "";
  58    }
  59}
  60
  61static std::u32string BuildTreeNodeLabel(VishwakarmaStorage::ObjectType objectType,
  62    const META_DATA* object, uint64_t memoryId) {
  63    if (VishwakarmaStorage::IsLogicalObjectType(objectType)) {
  64        const char* name = LogicalObjectName(objectType, object);
  65        if (name && name[0] != '\0') return DataTreeView::AsciiToDisplayText(name);
  66        return DataTreeView::AsciiToDisplayText(VishwakarmaStorage::ObjectTypeDisplayName(objectType));
  67    }
  68
  69    std::u32string label = DataTreeView::AsciiToDisplayText(
  70        VishwakarmaStorage::ObjectTypeDisplayName(objectType));
  71    label.push_back(U' ');
  72    std::u32string idText = DataTreeView::UInt64ToDecimalText(memoryId);
  73    label.append(idText);
  74    return label;
  75}
  76
  77static UIAtlasRegion MakeAtlasRegion(int x, int y, int w, int h, int atlasW, int atlasH) {
  78    UIAtlasRegion region{};
  79    region.uvMinX = (float)x / (float)atlasW;
  80    region.uvMinY = (float)y / (float)atlasH;
  81    region.uvMaxX = (float)(x + w) / (float)atlasW;
  82    region.uvMaxY = (float)(y + h) / (float)atlasH;
  83    return region;
  84}
  85
  86UIColors uiLightDefaultColors, uiActiveColors; // Initialized to default light theme colors.
  87
  88static void GenerateRoundedRectangleNineSlice(AtlasBitmap& atlas, int originX, int originY,
  89    int sourceSizePx, int sourceRadiusPx, UIRoundedRectangleNineSlice& outSlice) {
  90    const int atlasW = atlas.width;
  91    const int atlasH = atlas.height;
  92
  93    for (int y = 0; y < sourceSizePx; ++y) {
  94        for (int x = 0; x < sourceSizePx; ++x) {
  95            const float px = (float)x + 0.5f;
  96            const float py = (float)y + 0.5f;
  97            const float nearestX = std::clamp(px, (float)sourceRadiusPx,
  98                (float)(sourceSizePx - sourceRadiusPx));
  99            const float nearestY = std::clamp(py, (float)sourceRadiusPx,
 100                (float)(sourceSizePx - sourceRadiusPx));
 101            const float dx = px - nearestX;
 102            const float dy = py - nearestY;
 103            const float distance = std::sqrt(dx * dx + dy * dy);
 104            const float coverage = std::clamp((float)sourceRadiusPx + 0.5f - distance, 0.0f, 1.0f);
 105
 106            atlas.pixels[(originY + y) * atlasW + (originX + x)] = (uint8_t)std::round(coverage * 255.0f);
 107        }
 108    }
 109
 110    const int middle = sourceSizePx - 2 * sourceRadiusPx;
 111    const int widths[3] = { sourceRadiusPx, middle, sourceRadiusPx };
 112    const int heights[3] = { sourceRadiusPx, middle, sourceRadiusPx };
 113    int yCursor = originY;
 114    for (int row = 0; row < 3; ++row) {
 115        int xCursor = originX;
 116        for (int col = 0; col < 3; ++col) {
 117            outSlice.regions[row][col] =
 118                MakeAtlasRegion(xCursor, yCursor, widths[col], heights[row], atlasW, atlasH);
 119            xCursor += widths[col];
 120        }
 121        yCursor += heights[row];
 122    }
 123}
 124
 125static void FillRect(AtlasBitmap& atlas, int x, int y, int w, int h, uint8_t coverage) {
 126    for (int yy = y; yy < y + h; ++yy) {
 127        for (int xx = x; xx < x + w; ++xx) {
 128            atlas.pixels[yy * atlas.width + xx] = coverage;
 129        }
 130    }
 131}
 132
 133static bool IsPrivateUseCodepoint(char32_t codepoint) {
 134    return (codepoint >= 0xE000 && codepoint <= 0xF8FF) ||
 135        (codepoint >= 0xF0000 && codepoint <= 0xFFFFD) ||
 136        (codepoint >= 0x100000 && codepoint <= 0x10FFFD);
 137}
 138
 139static bool TryReserveIconCell(int iconIndex, int atlasW, int atlasH, int& outX, int& outY) {
 140    constexpr int iconCellSize = 24;
 141    constexpr int iconCellGap = 4;
 142    constexpr int iconStartY = 48;
 143    const int cellsPerRow = (atlasW + iconCellGap) / (iconCellSize + iconCellGap);
 144    if (cellsPerRow <= 0) return false;
 145
 146    outX = (iconIndex % cellsPerRow) * (iconCellSize + iconCellGap);
 147    outY = iconStartY + (iconIndex / cellsPerRow) * (iconCellSize + iconCellGap);
 148    return outX + iconCellSize <= atlasW && outY + iconCellSize <= atlasH;
 149}
 150
 151static void StoreIconCellGlyph(char32_t codepoint, int x, int y, int atlasW, int atlasH) {
 152    constexpr int iconCellSize = 24;
 153    Glyph glyph{};
 154    glyph.uvMinX = (float)x / atlasW;
 155    glyph.uvMinY = (float)y / atlasH;
 156    glyph.uvMaxX = (float)(x + iconCellSize) / atlasW;
 157    glyph.uvMaxY = (float)(y + iconCellSize) / atlasH;
 158    glyph.width = iconCellSize;
 159    glyph.height = iconCellSize;
 160    glyph.advanceX = iconCellSize;
 161    iconGlyphLookup[codepoint] = glyph;
 162    gIconAtlasMetadata.mixedIconCodepoints.push_back(codepoint);
 163}
 164
 165static AtlasBitmap BuildIconAtlas() {
 166    constexpr int atlasW = 256;
 167    constexpr int atlasH = 256;
 168    constexpr int iconCellSize = 24;
 169    constexpr int proceduralIconDrawSize = 20;
 170    AtlasBitmap atlas{};
 171    atlas.width = atlasW;
 172    atlas.height = atlasH;
 173    atlas.pixels.resize(atlasW * atlasH, 0);
 174
 175    // The source rounded rectangle is split into 9 texture regions at draw time.
 176    // Destination corners are resized to ~2 mm in screen space by PushRoundedRectangle.
 177    GenerateRoundedRectangleNineSlice(atlas, 0, 0, 32, 8, gIconAtlasMetadata.roundedRectangle);
 178
 179    iconGlyphLookup.clear();
 180    gIconAtlasMetadata.mixedIconCodepoints.clear();
 181
 182    std::array<int, 4> iconXs{};
 183    std::array<int, 4> iconYs{};
 184    for (int i = 0; i < 4; ++i) {
 185        if (!TryReserveIconCell(i, atlasW, atlasH, iconXs[i], iconYs[i])) continue;
 186        StoreIconCellGlyph(gIconAtlasMetadata.dummyIconCodepoints[i], iconXs[i], iconYs[i], atlasW, atlasH);
 187    }
 188
 189    // Dummy icon 0: plus
 190    FillRect(atlas, iconXs[0] + 10, iconYs[0] + 4, 4, 16, 255);
 191    FillRect(atlas, iconXs[0] + 4, iconYs[0] + 10, 16, 4, 255);
 192
 193    // Dummy icon 1: folder-like block
 194    FillRect(atlas, iconXs[1] + 4, iconYs[1] + 8, 16, 11, 255);
 195    FillRect(atlas, iconXs[1] + 6, iconYs[1] + 5, 7, 4, 255);
 196
 197    // Dummy icon 2: ring
 198    for (int y = 0; y < proceduralIconDrawSize; ++y) {
 199        for (int x = 0; x < proceduralIconDrawSize; ++x) {
 200            const float dx = (float)x + 0.5f - 10.0f;
 201            const float dy = (float)y + 0.5f - 10.0f;
 202            const float d = std::sqrt(dx * dx + dy * dy);
 203            if (d >= 5.0f && d <= 8.0f) {
 204                atlas.pixels[(iconYs[2] + 2 + y) * atlasW + (iconXs[2] + 2 + x)] = 255;
 205            }
 206        }
 207    }
 208
 209    // Dummy icon 3: 2x2 grid
 210    FillRect(atlas, iconXs[3] + 4, iconYs[3] + 4, 7, 7, 255);
 211    FillRect(atlas, iconXs[3] + 13, iconYs[3] + 4, 7, 7, 255);
 212    FillRect(atlas, iconXs[3] + 4, iconYs[3] + 13, 7, 7, 255);
 213    FillRect(atlas, iconXs[3] + 13, iconYs[3] + 13, 7, 7, 255);
 214
 215    if (ftIconFace) {
 216        FT_Set_Pixel_Sizes(ftIconFace, 0, proceduralIconDrawSize);
 217
 218        FT_UInt glyphIndex = 0;
 219        FT_ULong charCode = FT_Get_First_Char(ftIconFace, &glyphIndex);
 220        int iconIndex = (int)gIconAtlasMetadata.mixedIconCodepoints.size();
 221        while (glyphIndex != 0) {
 222            const char32_t codepoint = (char32_t)charCode;
 223            if (IsPrivateUseCodepoint(codepoint) &&
 224                std::find(gIconAtlasMetadata.dummyIconCodepoints.begin(),
 225                    gIconAtlasMetadata.dummyIconCodepoints.end(), codepoint) ==
 226                gIconAtlasMetadata.dummyIconCodepoints.end() &&
 227                FT_Load_Char(ftIconFace, charCode, FT_LOAD_RENDER) == 0) {
 228                int cellX = 0;
 229                int cellY = 0;
 230                if (!TryReserveIconCell(iconIndex, atlasW, atlasH, cellX, cellY)) break;
 231
 232                FT_GlyphSlot g = ftIconFace->glyph;
 233                const int bitmapX = cellX + std::max(0, (iconCellSize - (int)g->bitmap.width) / 2);
 234                const int bitmapY = cellY + std::max(0, (iconCellSize - (int)g->bitmap.rows) / 2);
 235                const int copyW = std::min((int)g->bitmap.width, iconCellSize);
 236                const int copyH = std::min((int)g->bitmap.rows, iconCellSize);
 237                for (int y = 0; y < copyH; ++y) {
 238                    for (int x = 0; x < copyW; ++x) {
 239                        atlas.pixels[(bitmapY + y) * atlasW + (bitmapX + x)] =
 240                            g->bitmap.buffer[y * g->bitmap.pitch + x];
 241                    }
 242                }
 243
 244                StoreIconCellGlyph(codepoint, cellX, cellY, atlasW, atlasH);
 245                ++iconIndex;
 246            }
 247
 248            charCode = FT_Get_Next_Char(ftIconFace, charCode, &glyphIndex);
 249        }
 250    }
 251
 252    return atlas;
 253}
 254
 255static AtlasBitmap BuildMSDFFontAtlas() {
 256    AtlasBitmap atlas{};
 257    atlas.width = NotoSansMSDF_Width;
 258    atlas.height = NotoSansMSDF_Height;
 259    atlas.bytesPerPixel = 4;
 260    atlas.pixels.assign(NotoSansMSDF_Pixels,
 261        NotoSansMSDF_Pixels + (size_t)atlas.width * (size_t)atlas.height * (size_t)atlas.bytesPerPixel);
 262
 263    glyphLookup.clear();
 264    for (const auto& entry : NotoSansMSDF_Glyphs) {
 265        const char32_t codepoint = entry.first;
 266        const MSDFGlyph& msdf = entry.second;
 267
 268        Glyph glyph{};
 269        glyph.uvMinX = msdf.atlasLeft / (float)atlas.width;
 270        glyph.uvMaxX = msdf.atlasRight / (float)atlas.width;
 271        glyph.uvMinY = ((float)atlas.height - msdf.atlasTop) / (float)atlas.height;
 272        glyph.uvMaxY = ((float)atlas.height - msdf.atlasBottom) / (float)atlas.height;
 273
 274        glyph.width = std::max(0, (int)std::ceil((msdf.planeRight - msdf.planeLeft) * NotoSansMSDF_Size));
 275        glyph.height = std::max(0, (int)std::ceil((msdf.planeTop - msdf.planeBottom) * NotoSansMSDF_Size));
 276        glyph.bearingX = (int)std::floor(msdf.planeLeft * NotoSansMSDF_Size);
 277        glyph.bearingY = (int)std::ceil(msdf.planeTop * NotoSansMSDF_Size);
 278        glyph.advanceX = std::max(0, (int)std::round(msdf.advance * NotoSansMSDF_Size));
 279
 280        glyphLookup[codepoint] = glyph;
 281    }
 282
 283    return atlas;
 284}
 285
 286static uint32_t StableRandomUIColour(uint32_t seed) {
 287    seed ^= seed >> 16;
 288    seed *= 0x7FEB352Du;
 289    seed ^= seed >> 15;
 290    seed *= 0x846CA68Bu;
 291    seed ^= seed >> 16;
 292
 293    uint32_t r = 80u + ((seed >> 0) & 0x7Fu);
 294    uint32_t g = 80u + ((seed >> 8) & 0x7Fu);
 295    uint32_t b = 80u + ((seed >> 16) & 0x7Fu);
 296    return 0xFF000000u | (b << 16) | (g << 8) | r;
 297}
 298
 299bool SubmitTextureUpload(const TextureUploadDesc& desc,
 300    ComPtr<ID3D12Resource>* outTex,  std::atomic<uint64_t>* fenceOut) {
 301
 302    uint32_t index = gUploadQueue.writeIndex.fetch_add(1, std::memory_order_relaxed);
 303    UploadRequest& req = gUploadQueue.requests[index % MAX_UPLOAD_REQUESTS];
 304
 305    req.type = UploadType::Texture2D;
 306    req.texture = desc;
 307    req.outResource = outTex;
 308    req.completionFence = fenceOut;
 309
 310    return true;
 311}
 312
 313bool UploadUIAtlasTexture(DX12ResourcesUI& uiRes, ID3D12Device* device, uint32_t atlasSlot,
 314    const AtlasBitmap& atlas) {
 315    if (!device || atlasSlot >= UI_MAX_ATLAS_TEXTURES || atlas.width <= 0 || atlas.height <= 0 ||
 316        atlas.pixels.empty() || (atlas.bytesPerPixel != 1 && atlas.bytesPerPixel != 4)) {
 317        return false;
 318    }
 319
 320    TextureUploadDesc desc = {};
 321    desc.width = atlas.width;
 322    desc.height = atlas.height;
 323    desc.format = atlas.bytesPerPixel == 4 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM;
 324    desc.pixels = atlas.pixels.data();
 325    desc.rowPitch = atlas.width * atlas.bytesPerPixel;
 326
 327    std::atomic<uint64_t> uploadFence = 0;
 328    SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[atlasSlot], &uploadFence);
 329
 330    uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
 331    uploadFence.store(atlasReadyFence, std::memory_order_release);
 332    toCopyThreadCV.notify_one();
 333
 334    if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
 335        ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
 336        WaitForSingleObject(gpu.copyFenceEvent, INFINITE);
 337    }
 338
 339    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
 340    srvDesc.Format = desc.format;
 341    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
 342    srvDesc.Texture2D.MipLevels = 1;
 343    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
 344
 345    CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart(),
 346        atlasSlot, device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
 347    device->CreateShaderResourceView(uiRes.uiAtlasTextures[atlasSlot].Get(), &srvDesc, srvHandle);
 348    return true;
 349}
 350
 351void InitUIResources( DX12ResourcesUI& uiRes, ID3D12Device* device) {
 352    if (!InitFontSystem()) { // FONT system initialization (CPU-side)
 353        std::cerr << "Font system initialization failed failed\n";
 354        return;
 355    }
 356
 357    // Root signature
 358    CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; // Descriptor ranges
 359
 360    // Range 0: SRV (t0)  → from srvHeap // 1: 1 Texture, 0: register t0 = atlas
 361    ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, UI_MAX_ATLAS_TEXTURES, 0, 0,
 362        D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
 363    // Range 1: SAMPLER (s0) → from samplerHeap // 1: 1 Sampler, 0: register s0 = sampler
 364    ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_NONE);
 365
 366    CD3DX12_ROOT_PARAMETER1 rootParams[3];
 367    rootParams[0].InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_NONE,
 368        D3D12_SHADER_VISIBILITY_VERTEX);// b0 - Ortho constant buffer (vertex shader)
 369
 370    // Root Parameter 1: Descriptor Table containing only the SRV
 371    rootParams[1].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
 372
 373    // Root Parameter 2: Descriptor Table containing only the SAMPLER
 374    rootParams[2].InitAsDescriptorTable(1, &ranges[1],
 375        D3D12_SHADER_VISIBILITY_PIXEL);
 376
 377    CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootDesc;
 378    rootDesc.Init_1_1(_countof(rootParams), rootParams, 0, nullptr,
 379        D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
 380
 381    ComPtr<ID3DBlob> signature;
 382    ComPtr<ID3DBlob> errorBlob;
 383
 384    HRESULT hr = D3DX12SerializeVersionedRootSignature(&rootDesc,
 385        D3D_ROOT_SIGNATURE_VERSION_1_1, &signature, &errorBlob);
 386    if (FAILED(hr)) {
 387        if (errorBlob)
 388            std::cerr << "Root Signature Serialization Failed:\n"
 389            << (char*)errorBlob->GetBufferPointer() << std::endl;
 390        ThrowIfFailed(hr); // will print the real error
 391    }
 392
 393    ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(),
 394        signature->GetBufferSize(), IID_PPV_ARGS(&uiRes.uiRootSignature)));
 395    
 396    // Create SRV descriptor heap (1 texture)
 397    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
 398    heapDesc.NumDescriptors = UI_MAX_ATLAS_TEXTURES;
 399    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
 400    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
 401    ThrowIfFailed(device->CreateDescriptorHeap( &heapDesc, IID_PPV_ARGS(&uiRes.srvHeap) ));
 402
 403    // Create SAMPLER descriptor heap (shader-visible)
 404    D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
 405    samplerHeapDesc.NumDescriptors = 1;
 406    samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
 407    samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
 408    ThrowIfFailed(device->CreateDescriptorHeap(&samplerHeapDesc,
 409        IID_PPV_ARGS(&uiRes.samplerHeap)));
 410
 411    // Create the actual sampler.
 412    D3D12_SAMPLER_DESC samplerDesc = {};
 413    samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
 414    samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 415    samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 416    samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
 417    samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
 418    samplerDesc.MinLOD = 0;
 419    samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
 420
 421    device->CreateSampler(&samplerDesc,
 422        uiRes.samplerHeap->GetCPUDescriptorHandleForHeapStart());
 423
 424    // Shaders are compiled to DXIL during the build and embedded into the executable.
 425    // Input layout
 426
 427    D3D12_INPUT_ELEMENT_DESC layout[] = {
 428        { "POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 429        { "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 430        { "COLOR",0,DXGI_FORMAT_R32_UINT,0,16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 },
 431        { "TEXCOORD",1,DXGI_FORMAT_R32_UINT,0,20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0 }
 432    };
 433
 434    // PSO
 435    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
 436    psoDesc.InputLayout = { layout,_countof(layout) };
 437    psoDesc.pRootSignature = uiRes.uiRootSignature.Get();
 438    psoDesc.VS = CD3DX12_SHADER_BYTECODE(g_uiVertexShader, sizeof(g_uiVertexShader));
 439    psoDesc.PS = CD3DX12_SHADER_BYTECODE(g_uiPixelShader, sizeof(g_uiPixelShader));
 440    psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
 441    psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
 442    psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
 443    psoDesc.BlendState.RenderTarget[0].BlendEnable = TRUE;
 444    psoDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
 445    psoDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
 446    psoDesc.DepthStencilState.DepthEnable = FALSE;
 447    psoDesc.SampleMask = UINT_MAX;
 448    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
 449    psoDesc.NumRenderTargets = 1;
 450    psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
 451    psoDesc.SampleDesc.Count = 1;
 452
 453    ThrowIfFailed( device->CreateGraphicsPipelineState( &psoDesc, IID_PPV_ARGS(&uiRes.uiPSO)));
 454    
 455    // Vertex buffer
 456    auto uploadHeap = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
 457
 458    auto vbDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxVertices * sizeof(UIVertex));
 459    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &vbDesc,
 460            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiVertexBuffer)));
 461
 462    auto ibDesc = CD3DX12_RESOURCE_DESC::Buffer( uiRes.maxIndices * sizeof(uint16_t));
 463    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &ibDesc,
 464            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiIndexBuffer)));
 465
 466    auto cbDesc = CD3DX12_RESOURCE_DESC::Buffer(256);
 467    ThrowIfFailed( device->CreateCommittedResource( &uploadHeap, D3D12_HEAP_FLAG_NONE, &cbDesc,
 468            D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uiRes.uiOrthoConstantBuffer)));
 469
 470    CD3DX12_RANGE readRange(0, 0);
 471    uiRes.uiVertexBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pVertexDataBegin));
 472    uiRes.uiIndexBuffer->Map(  0, &readRange, reinterpret_cast<void**>(&uiRes.pIndexDataBegin));
 473    uiRes.uiOrthoConstantBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&uiRes.pOrthoDataBegin));
 474    std::wcout << L"UI Resources Initialized (Phase 4A)\n";
 475    
 476    AtlasBitmap englishAtlas = BuildMSDFFontAtlas();
 477    AtlasBitmap iconAtlas = BuildIconAtlas();
 478    
 479    TextureUploadDesc desc = {};
 480    desc.width = englishAtlas.width;
 481    desc.height = englishAtlas.height;
 482    desc.format = DXGI_FORMAT_R8G8B8A8_UNORM;
 483    desc.pixels = englishAtlas.pixels.data();
 484    desc.rowPitch = englishAtlas.width * englishAtlas.bytesPerPixel;
 485
 486    int bytesPerPixel = 1;
 487
 488    SubmitTextureUpload(desc, &uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT], &atlasFence);// Enqueue the upload through upload queue
 489    // RESERVED FENCE VALUE FOR THIS UPLOAD (this is the key change)
 490    // The copy thread MUST eventually signal exactly this value.
 491    uint64_t atlasReadyFence = gpu.copyFenceValue.fetch_add(1, std::memory_order_relaxed);
 492    // Tell everyone (including the render thread) what fence value to wait for
 493    atlasFence.store(atlasReadyFence, std::memory_order_release);
 494	toCopyThreadCV.notify_one(); // Wakeup CPU thread to process the newly uploaded texture.
 495    // CPU-blocking wait until Copy Queue has processed this upload
 496    if (gpu.copyFence->GetCompletedValue() < atlasReadyFence) {
 497        ThrowIfFailed(gpu.copyFence->SetEventOnCompletion(atlasReadyFence, gpu.copyFenceEvent));
 498        WaitForSingleObject(gpu.copyFenceEvent, INFINITE);   // CPU blocks here
 499    }
 500
 501    // Now the texture is in DEFAULT heap → safe to create SRV
 502    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
 503    srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
 504    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
 505    srvDesc.Texture2D.MipLevels = 1;
 506    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
 507    
 508    device->CreateShaderResourceView(uiRes.uiAtlasTextures[UI_ENGLISH_ATLAS_SLOT].Get(), &srvDesc,
 509        uiRes.srvHeap->GetCPUDescriptorHandleForHeapStart());
 510
 511    SaveToBmp("icon_atlas_debug.bmp", iconAtlas.pixels.data(),
 512        iconAtlas.width, iconAtlas.height, bytesPerPixel);
 513    UploadUIAtlasTexture(uiRes, device, UI_ICON_ATLAS_SLOT, iconAtlas);
 514
 515    std::wcout << L"Mandatory UI atlases uploaded: English slot " << UI_ENGLISH_ATLAS_SLOT
 516        << L", icon slot " << UI_ICON_ATLAS_SLOT << L"\n";
 517}
 518
 519// Cleanup
 520void CleanupUIResources(DX12ResourcesUI& uiRes) {
 521    if (uiRes.uiVertexBuffer) uiRes.uiVertexBuffer->Unmap(0, nullptr);
 522    if (uiRes.uiIndexBuffer) uiRes.uiIndexBuffer->Unmap(0, nullptr);
 523    if (uiRes.uiOrthoConstantBuffer) uiRes.uiOrthoConstantBuffer->Unmap(0, nullptr);
 524
 525    uiRes = {};
 526}
 527
 528static float TextScaleForHeight(float targetHeight) {
 529    auto glyphIt = glyphLookup.find(U'M');
 530    if (glyphIt == glyphLookup.end() || glyphIt->second.height <= 0) return 1.0f;
 531
 532    return targetHeight / (float)glyphIt->second.height;
 533}
 534
 535static float MeasureUIStringWidth(const char32_t* text, float scale) {
 536    if (!text) return 0.0f;
 537
 538    float cursorX = 0.0f;
 539    float maxRight = 0.0f;
 540
 541    for (const char32_t* p = text; *p; ++p) {
 542        auto glyphIt = glyphLookup.find(*p);
 543        if (glyphIt == glyphLookup.end()) continue;
 544
 545        const Glyph& g = glyphIt->second;
 546        float glyphLeft = cursorX + (float)g.bearingX * scale;
 547        float glyphRight = glyphLeft + (float)g.width * scale;
 548        if (glyphRight > maxRight) maxRight = glyphRight;
 549        cursorX += (float)g.advanceX * scale;
 550    }
 551
 552    return std::max(cursorX, maxRight);
 553}
 554
 555static const char32_t* LocalizedUIString(UITextID stringID) {
 556    const char32_t* text = GetUILocalizedString(stringID, UILanguage::English);
 557    return text ? text : U"";
 558}
 559
 560static const char32_t* LocalizedControlLabel(const UIControlDefinition& ctrl) {
 561    const char32_t* label = LocalizedUIString(ctrl.nameStringID);
 562    if (*label != U'\0') return label;
 563
 564    if (ctrl.action == Commands::INVALID || ctrl.type == 0 || ctrl.type == 3) {
 565        return LocalizedUIString(ctrl.nameStringID);
 566    }
 567
 568    return U"";
 569}
 570
 571static void ClampTopRibbonScroll(UITopRibbonLayout& layout, float viewportWidth) {
 572    const float maxScroll = std::max(0.0f, layout.totalContentWidthPx - viewportWidth);
 573    layout.scrollOffsetPx = std::clamp(layout.scrollOffsetPx, 0.0f, maxScroll);
 574}
 575
 576void PrecomputeTopRibbonLayout(UITopRibbonLayout& layout, float monitorDPIX, float monitorDPIY) {
 577    const float previousScroll = layout.scrollOffsetPx;
 578    layout = UITopRibbonLayout{};
 579
 580    layout.dpiX = monitorDPIX;
 581    layout.dpiY = monitorDPIY;
 582
 583    const float pixelsPerMMx = monitorDPIX / 25.4f;
 584    const float pixelsPerMMy = monitorDPIY / 25.4f;
 585    layout.buttonWidthPx = std::round(UI_BUTTON_WIDTH_MM * pixelsPerMMx);
 586    layout.iconSizePx = std::round(UI_ICON_SIZE_MM * pixelsPerMMy);
 587    layout.textHeightPx = std::round(UI_TEXT_HEIGHT_MM * pixelsPerMMy);
 588    layout.buttonHeightPx = std::max(std::round(UI_BUTTON_HEIGHT_MM * pixelsPerMMy),
 589        std::max(layout.iconSizePx, layout.textHeightPx) + 4.0f);
 590    layout.iconReservedWidthPx = layout.iconSizePx + 4.0f;
 591    layout.textStartOffsetPx = layout.iconReservedWidthPx + 4.0f;
 592    layout.textEndInsetPx = 6.0f;
 593    layout.buttonGapPx = UI_BUTTON_GAP_MM * pixelsPerMMx;
 594    layout.tabBarHeightPx = std::round(UI_TAB_BAR_HEIGHT_MM * pixelsPerMMy);
 595    layout.roundedCornerRadiusPx = std::max(1.0f, std::round(UI_BUTTON_CORNER_RADIUS_MM * pixelsPerMMy));
 596    layout.uiTextScale = TextScaleForHeight(layout.textHeightPx);
 597    layout.actionGroupLabelY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy;
 598    layout.actionGroupLabelHeightPx = UI_ACTION_GROUP_LABEL_HEIGHT_MM * pixelsPerMMy;
 599    layout.topActionGroupY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 600        UI_ACTION_GROUP_LABEL_HEIGHT_MM) * pixelsPerMMy + 5.0f;
 601    layout.actionSubGroupLabelY = (UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 602        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 603        UI_ACTION_GROUP_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy + 5.0f;
 604    layout.topUITotalHeightPx = std::round((UI_TAB_BAR_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 605        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 606        UI_ACTION_GROUP_HEIGHT_MM + UI_DIVIDER_GAP_PX +
 607        UI_ACTION_GROUP_LABEL_HEIGHT_MM + UI_DIVIDER_GAP_PX) * pixelsPerMMy) + 7.0f;
 608
 609
 610    const float groupNavPaddingPx = 8.0f;
 611    const float groupNavGapPx = 2.0f * pixelsPerMMx;
 612    float groupNavX = 0.0f;
 613    for (size_t i = 0; i < TotalTopUIActionGroups; ++i) {
 614        const char32_t* label = LocalizedUIString(topUIActionGroupNames[i].labelStringID);
 615        layout.actionGroups[i].navX = groupNavX;
 616        layout.actionGroups[i].navWidth = std::ceil(MeasureUIStringWidth(label, layout.uiTextScale) +
 617            2.0f * groupNavPaddingPx);
 618        groupNavX += layout.actionGroups[i].navWidth + groupNavGapPx;
 619    }
 620    layout.actionGroupNavTotalWidthPx = groupNavX > 0.0f ? groupNavX - groupNavGapPx : 0.0f;
 621
 622    std::array<bool, TotalTopUIActionGroups> groupSeen{};
 623    float currentX = 5.0f;
 624    float verticalSlotMaxSize = 0.0f;
 625    float contentRight = currentX;
 626    int activeSubGroupIndex = -1;
 627    size_t activeSubGroupRun = 0;
 628
 629    for (size_t i = 0; i < TotalUIControls; ++i) {
 630        const UIControlDefinition& ctrl = AllUIControls[i];
 631        const size_t groupIndex = ctrl.actionGroupIndex;
 632        const size_t subGroupIndex = ctrl.actionSubGroupIndex;
 633
 634        if (groupIndex < TotalTopUIActionGroups && !groupSeen[groupIndex]) {
 635            layout.actionGroups[groupIndex].contentStartX = currentX;
 636            layout.actionGroups[groupIndex].contentEndX = currentX;
 637            groupSeen[groupIndex] = true;
 638        }
 639
 640        if (subGroupIndex < TotalTopUIActionSubGroups &&
 641            !layout.actionSubGroups[subGroupIndex].hasControls) {
 642            layout.actionSubGroups[subGroupIndex].contentStartX = currentX;
 643            layout.actionSubGroups[subGroupIndex].contentEndX = currentX;
 644            layout.actionSubGroups[subGroupIndex].hasControls = true;
 645        }
 646
 647        if (activeSubGroupIndex != (int)subGroupIndex &&
 648            layout.actionSubGroupRunCount < layout.actionSubGroupRuns.size()) {
 649            activeSubGroupIndex = (int)subGroupIndex;
 650            activeSubGroupRun = layout.actionSubGroupRunCount++;
 651            layout.actionSubGroupRuns[activeSubGroupRun].subGroupIndex = ctrl.actionSubGroupIndex;
 652            layout.actionSubGroupRuns[activeSubGroupRun].contentStartX = currentX;
 653            layout.actionSubGroupRuns[activeSubGroupRun].contentEndX = currentX;
 654        }
 655
 656        float btnWidth = layout.buttonWidthPx;
 657        float btnY = layout.topActionGroupY;
 658        const char32_t* label = LocalizedControlLabel(ctrl);
 659        if (ctrl.showText && *label != U'\0') {
 660            float contentWidth = layout.textStartOffsetPx +
 661                MeasureUIStringWidth(label, layout.uiTextScale) + layout.textEndInsetPx;
 662            btnWidth = std::max(btnWidth, contentWidth);
 663        }
 664        if (ctrl.noOfVerticalSlots > 1) {
 665            btnY += ctrl.verticalSlotNo * (layout.buttonHeightPx + 1.0f);
 666        }
 667
 668        layout.controls[i] = { currentX, btnY, btnWidth, layout.buttonHeightPx };
 669        verticalSlotMaxSize = std::max(verticalSlotMaxSize, btnWidth);
 670
 671        const bool endOfColumn = (i + 1 == TotalUIControls) || (AllUIControls[i + 1].verticalSlotNo == 0);
 672        if (endOfColumn) {
 673            const float columnRight = currentX + verticalSlotMaxSize;
 674            if (groupIndex < TotalTopUIActionGroups) {
 675                layout.actionGroups[groupIndex].contentEndX =
 676                    std::max(layout.actionGroups[groupIndex].contentEndX, columnRight);
 677            }
 678            if (subGroupIndex < TotalTopUIActionSubGroups) {
 679                layout.actionSubGroups[subGroupIndex].contentEndX =
 680                    std::max(layout.actionSubGroups[subGroupIndex].contentEndX, columnRight);
 681            }
 682            if (layout.actionSubGroupRunCount > 0) {
 683                layout.actionSubGroupRuns[activeSubGroupRun].contentEndX =
 684                    std::max(layout.actionSubGroupRuns[activeSubGroupRun].contentEndX, columnRight);
 685            }
 686            contentRight = std::max(contentRight, columnRight);
 687
 688            if (i + 1 < TotalUIControls) {
 689                currentX = columnRight + layout.buttonGapPx;
 690            }
 691            verticalSlotMaxSize = 0.0f;
 692        }
 693    }
 694
 695    for (size_t gIdx = 0; gIdx < TotalTopUIActionGroups; ++gIdx) {
 696        layout.actionGroups[gIdx].contentWidth = std::max(0.0f, layout.actionGroups[gIdx].contentEndX - layout.actionGroups[gIdx].contentStartX);
 697    }
 698
 699    layout.totalContentWidthPx = contentRight + 30.0f;
 700    layout.scrollOffsetPx = previousScroll;
 701    layout.isValid = true;
 702}
 703
 704static float MapRibbonToNav(float x, const UITopRibbonLayout& layout) {
 705    if (TotalTopUIActionGroups == 0) return 0.0f;
 706
 707    const auto& firstGroup = layout.actionGroups[0];
 708    if (x <= firstGroup.contentStartX) {
 709        return firstGroup.navX;
 710    }
 711    const auto& lastGroup = layout.actionGroups[TotalTopUIActionGroups - 1];
 712    if (x >= lastGroup.contentEndX) {
 713        return lastGroup.navX + lastGroup.navWidth;
 714    }
 715
 716    for (size_t i = 0; i < TotalTopUIActionGroups; ++i) {
 717        const auto& grp = layout.actionGroups[i];
 718        if (x >= grp.contentStartX && x <= grp.contentEndX) {
 719            float width = grp.contentWidth;
 720            if (width <= 0.0f) return grp.navX;
 721            float t = (x - grp.contentStartX) / width;
 722            return grp.navX + t * grp.navWidth;
 723        }
 724        if (i + 1 < TotalTopUIActionGroups) {
 725            const auto& nextGrp = layout.actionGroups[i + 1];
 726            if (x > grp.contentEndX && x < nextGrp.contentStartX) {
 727                float gapWidth = nextGrp.contentStartX - grp.contentEndX;
 728                if (gapWidth <= 0.0f) return nextGrp.navX;
 729                float t = (x - grp.contentEndX) / gapWidth;
 730                float startNav = grp.navX + grp.navWidth;
 731                float endNav = nextGrp.navX;
 732                return startNav + t * (endNav - startNav);
 733            }
 734        }
 735    }
 736
 737    return 0.0f;
 738}
 739
 740// PushRect
 741void PushRect( UIDrawContext& ctx, float x, float y, float w, float h,
 742    uint32_t color, DX12ResourcesUI& uiRes) {
 743    if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
 744    if (ctx.indexCount + 6 > uiRes.maxIndices) return;
 745
 746    uint16_t base = ctx.vertexCount;
 747
 748    ctx.vertexPtr[0] = { x,y,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 749    ctx.vertexPtr[1] = { x + w,y,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 750    ctx.vertexPtr[2] = { x + w,y + h,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 751    ctx.vertexPtr[3] = { x,y + h,0,0,color, UI_ENGLISH_ATLAS_SLOT };
 752
 753    ctx.indexPtr[0] = base + 0;
 754    ctx.indexPtr[1] = base + 1;
 755    ctx.indexPtr[2] = base + 2;
 756    ctx.indexPtr[3] = base + 0;
 757    ctx.indexPtr[4] = base + 2;
 758    ctx.indexPtr[5] = base + 3;
 759}
 760
 761static void PushTexturedQuad(UIDrawContext& ctx, float x, float y, float w, float h,
 762    const UIAtlasRegion& region, uint32_t atlasIndex, uint32_t color, DX12ResourcesUI& uiRes) {
 763    if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
 764    if (ctx.indexCount + 6 > uiRes.maxIndices) return;
 765
 766    uint16_t base = ctx.vertexCount;
 767    ctx.vertexPtr[0] = { x,     y,     region.uvMinX, region.uvMinY, color, atlasIndex };
 768    ctx.vertexPtr[1] = { x + w, y,     region.uvMaxX, region.uvMinY, color, atlasIndex };
 769    ctx.vertexPtr[2] = { x + w, y + h, region.uvMaxX, region.uvMaxY, color, atlasIndex };
 770    ctx.vertexPtr[3] = { x,     y + h, region.uvMinX, region.uvMaxY, color, atlasIndex };
 771
 772    ctx.indexPtr[0] = base + 0;
 773    ctx.indexPtr[1] = base + 1;
 774    ctx.indexPtr[2] = base + 2;
 775    ctx.indexPtr[3] = base + 0;
 776    ctx.indexPtr[4] = base + 2;
 777    ctx.indexPtr[5] = base + 3;
 778
 779    ctx.vertexPtr += 4;
 780    ctx.indexPtr += 6;
 781    ctx.vertexCount += 4;
 782    ctx.indexCount += 6;
 783}
 784
 785void PushRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
 786    uint32_t color, DX12ResourcesUI& uiRes) {
 787    if (w <= 0.0f || h <= 0.0f) return;
 788    if (ctx.vertexCount + 36 > uiRes.maxVertices) return;
 789    if (ctx.indexCount + 54 > uiRes.maxIndices) return;
 790
 791    const float clampedRadius = std::max(1.0f, std::min(radiusPx, 0.5f * std::min(w, h)));
 792    const float xCuts[4] = { x, x + clampedRadius, x + w - clampedRadius, x + w };
 793    const float yCuts[4] = { y, y + clampedRadius, y + h - clampedRadius, y + h };
 794
 795    /*
 796    for (int row = 0; row < 3; ++row) {
 797        for (int col = 0; col < 3; ++col) {
 798            PushTexturedQuad(ctx, xCuts[col], yCuts[row],
 799                xCuts[col + 1] - xCuts[col], yCuts[row + 1] - yCuts[row],
 800                gIconAtlasMetadata.roundedRectangle.regions[row][col],
 801                UI_ICON_ATLAS_SLOT, color, uiRes);
 802        }
 803    }*/
 804	// Unrolled the above loops for better performance (fewer function calls, better instruction-level parallelism)
 805    PushTexturedQuad(ctx, xCuts[0], yCuts[0], xCuts[1] - xCuts[0], yCuts[1] - yCuts[0],
 806        gIconAtlasMetadata.roundedRectangle.regions[0][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 807    PushTexturedQuad(ctx, xCuts[1], yCuts[0], xCuts[2] - xCuts[1], yCuts[1] - yCuts[0],
 808        gIconAtlasMetadata.roundedRectangle.regions[0][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 809    PushTexturedQuad(ctx, xCuts[2], yCuts[0], xCuts[3] - xCuts[2], yCuts[1] - yCuts[0],
 810        gIconAtlasMetadata.roundedRectangle.regions[0][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 811    PushTexturedQuad(ctx, xCuts[0], yCuts[1], xCuts[1] - xCuts[0], yCuts[2] - yCuts[1],
 812        gIconAtlasMetadata.roundedRectangle.regions[1][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 813    PushTexturedQuad(ctx, xCuts[1], yCuts[1], xCuts[2] - xCuts[1], yCuts[2] - yCuts[1],
 814        gIconAtlasMetadata.roundedRectangle.regions[1][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 815    PushTexturedQuad(ctx, xCuts[2], yCuts[1], xCuts[3] - xCuts[2], yCuts[2] - yCuts[1],
 816        gIconAtlasMetadata.roundedRectangle.regions[1][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 817    PushTexturedQuad(ctx, xCuts[0], yCuts[2], xCuts[1] - xCuts[0], yCuts[3] - yCuts[2],
 818        gIconAtlasMetadata.roundedRectangle.regions[2][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 819    PushTexturedQuad(ctx, xCuts[1], yCuts[2], xCuts[2] - xCuts[1], yCuts[3] - yCuts[2],
 820        gIconAtlasMetadata.roundedRectangle.regions[2][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 821    PushTexturedQuad(ctx, xCuts[2], yCuts[2], xCuts[3] - xCuts[2], yCuts[3] - yCuts[2],
 822        gIconAtlasMetadata.roundedRectangle.regions[2][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 823}
 824
 825void PushTopRoundedRectangle(UIDrawContext& ctx, float x, float y, float w, float h, float radiusPx,
 826    uint32_t color, DX12ResourcesUI& uiRes) {
 827    if (w <= 0.0f || h <= 0.0f) return;
 828    if (ctx.vertexCount + 24 > uiRes.maxVertices) return;
 829    if (ctx.indexCount + 36 > uiRes.maxIndices) return;
 830
 831    const float clampedRadius = std::max(1.0f, std::min(radiusPx, 0.5f * std::min(w, h)));
 832    const float xCuts[4] = { x, x + clampedRadius, x + w - clampedRadius, x + w };
 833    const float yCuts[3] = { y, y + clampedRadius, y + h };
 834
 835    // Row 0 (Top part with rounded corners)
 836    PushTexturedQuad(ctx, xCuts[0], yCuts[0], xCuts[1] - xCuts[0], yCuts[1] - yCuts[0],
 837        gIconAtlasMetadata.roundedRectangle.regions[0][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 838    PushTexturedQuad(ctx, xCuts[1], yCuts[0], xCuts[2] - xCuts[1], yCuts[1] - yCuts[0],
 839        gIconAtlasMetadata.roundedRectangle.regions[0][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 840    PushTexturedQuad(ctx, xCuts[2], yCuts[0], xCuts[3] - xCuts[2], yCuts[1] - yCuts[0],
 841        gIconAtlasMetadata.roundedRectangle.regions[0][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 842
 843    // Row 1 (Bottom part with sharp corners utilizing the flat middle row)
 844    PushTexturedQuad(ctx, xCuts[0], yCuts[1], xCuts[1] - xCuts[0], yCuts[2] - yCuts[1],
 845        gIconAtlasMetadata.roundedRectangle.regions[1][0], UI_ICON_ATLAS_SLOT, color, uiRes);
 846    PushTexturedQuad(ctx, xCuts[1], yCuts[1], xCuts[2] - xCuts[1], yCuts[2] - yCuts[1],
 847        gIconAtlasMetadata.roundedRectangle.regions[1][1], UI_ICON_ATLAS_SLOT, color, uiRes);
 848    PushTexturedQuad(ctx, xCuts[2], yCuts[1], xCuts[3] - xCuts[2], yCuts[2] - yCuts[1],
 849        gIconAtlasMetadata.roundedRectangle.regions[1][2], UI_ICON_ATLAS_SLOT, color, uiRes);
 850}
 851
 852static void PushIcon(UIDrawContext& ctx, float x, float y, float w, float h, char32_t iconCodepoint,
 853    uint32_t color, DX12ResourcesUI& uiRes) {
 854    auto iconIt = iconGlyphLookup.find(iconCodepoint);
 855    if (iconIt == iconGlyphLookup.end()) return;
 856    const Glyph& glyph = iconIt->second;
 857    UIAtlasRegion icon{ glyph.uvMinX, glyph.uvMinY, glyph.uvMaxX, glyph.uvMaxY };
 858    PushTexturedQuad(ctx, x, y, w, h, icon, UI_ICON_ATLAS_SLOT, color, uiRes);
 859}
 860
 861// Returns true if clicked this frame
 862bool PushInteractiveRect(UIDrawContext& ctx, float x, float y, float w, float h, uint32_t baseColor,
 863    uint32_t id, const UIInput& input, DX12ResourcesUI& uiRes, bool enabled = true) {
 864    uint32_t color = baseColor;
 865
 866    bool hovered = enabled && (input.mouseX >= x && input.mouseX < x + w &&
 867        input.mouseY >= y && input.mouseY < y + h);
 868
 869    if (hovered) color = 0xFF555555; // hover tint (TODO: theme-aware)
 870    if (hovered && input.leftButtonDown) color = 0xFF333333; // pressed tint
 871    if (!enabled) color = 0xFF1E1E1E; // If disabled, force a darker/grayer base color
 872    
 873    PushRect(ctx, x, y, w, h, color, uiRes);
 874
 875    if (!enabled) return false;// Disabled controls do NOT respond to clicks
 876    if (hovered && input.leftButtonPressedThisFrame) {
 877        return true;
 878    }
 879    return false;
 880}
 881
 882void PushText(UIDrawContext& ctx, float x, float y, const char* text, uint32_t color, DX12ResourcesUI& uiRes)
 883{
 884    float cursorX = x;
 885    uint32_t glyphCount = 0;
 886
 887    for (const char* p = text; *p; ++p)
 888    {
 889        // Bounds Checking (Crucial for text strings)
 890        if (ctx.vertexCount + (glyphCount + 1) * 4 > uiRes.maxVertices) return;
 891        if (ctx.indexCount + (glyphCount + 1) * 6 > uiRes.maxIndices) return;
 892
 893        char c = *p;
 894        if (glyphLookup.find(c) == glyphLookup.end()) continue;
 895
 896        const Glyph& g = glyphLookup[c];
 897        if (g.width <= 0 || g.height <= 0) {
 898            cursorX += g.advanceX;
 899            continue;
 900        }
 901
 902        float xpos = cursorX + g.bearingX;
 903        float ypos = y - g.bearingY;
 904        float w = (float)g.width;
 905        float h = (float)g.height;
 906        uint32_t vertexOffset = glyphCount * 4;
 907        uint32_t indexOffset = glyphCount * 6;
 908        
 909        // Add 4 vertices. Write relative to the current pointer (0, 1, 2, 3)
 910        uint32_t vidx = ctx.vertexCount + vertexOffset;
 911        ctx.vertexPtr[vertexOffset + 0] = { xpos,     ypos,     g.uvMinX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
 912        ctx.vertexPtr[vertexOffset + 1] = { xpos + w, ypos,     g.uvMaxX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
 913        ctx.vertexPtr[vertexOffset + 2] = { xpos + w, ypos + h, g.uvMaxX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
 914        ctx.vertexPtr[vertexOffset + 3] = { xpos,     ypos + h, g.uvMinX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
 915
 916        // Add 6 indices. Write indices relative to the current index pointer
 917        ctx.indexPtr[indexOffset + 0] = vidx + 0;
 918        ctx.indexPtr[indexOffset + 1] = vidx + 1;
 919        ctx.indexPtr[indexOffset + 2] = vidx + 2;
 920        ctx.indexPtr[indexOffset + 3] = vidx + 0;
 921        ctx.indexPtr[indexOffset + 4] = vidx + 2;
 922        ctx.indexPtr[indexOffset + 5] = vidx + 3;
 923
 924        glyphCount++;
 925        cursorX += g.advanceX;
 926    }
 927
 928    ctx.vertexPtr += glyphCount * 4;
 929    ctx.indexPtr += glyphCount * 6;
 930    ctx.vertexCount += glyphCount * 4;
 931    ctx.indexCount += glyphCount * 6;
 932}
 933
 934// This function renders the list of tabs, all top menu buttons (with dropdowns if required),
 935// side favorite / frequent buttons bars, right side property window, bottom status bar.
 936// This is also responsible for all relevant DirectX12 configurations required for rendering User Interface.
 937void RenderUIOverlay(SingleUIWindow& window, ID3D12GraphicsCommandList* cmd, DX12ResourcesUI& uiRes,
 938    UITopRibbonLayout& topRibbonLayout, float monitorDPIX, float monitorDPIY, const UIInput& input) {
 939    
 940    if (!cmd) return; //Defensive check.
 941
 942    cmd->SetPipelineState(uiRes.uiPSO.Get());
 943    cmd->SetGraphicsRootSignature(uiRes.uiRootSignature.Get());
 944
 945    // Bind descriptor heap
 946    ID3D12DescriptorHeap* heaps[] = { uiRes.srvHeap.Get(), uiRes.samplerHeap.Get() };
 947    cmd->SetDescriptorHeaps(_countof(heaps), heaps);
 948
 949    // Bind the descriptor table (which contains t0 + s0)    
 950    // Root Parameter 1 = SRV table// must match rootParams[1]
 951    cmd->SetGraphicsRootDescriptorTable(1, uiRes.srvHeap->GetGPUDescriptorHandleForHeapStart());
 952    // Root Parameter 2 = Sampler table
 953    cmd->SetGraphicsRootDescriptorTable(2, uiRes.samplerHeap->GetGPUDescriptorHandleForHeapStart());
 954    // Bind ortho constant buffer (still root parameter 0)
 955    cmd->SetGraphicsRootConstantBufferView(0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
 956
 957    float W = (float)window.dx.WindowWidth;
 958    float H = (float)window.dx.WindowHeight;
 959    float* ortho = (float*)uiRes.pOrthoDataBegin;
 960
 961    ortho[0] = 2 / W; ortho[1] = 0;   ortho[2] = 0; ortho[3] = -1;
 962    ortho[4] = 0;   ortho[5] = -2 / H; ortho[6] = 0; ortho[7] = 1;
 963    ortho[8] = 0;   ortho[9] = 0;   ortho[10] = 1; ortho[11] = 0;
 964    ortho[12] = 0;  ortho[13] = 0;  ortho[14] = 0; ortho[15] = 1;
 965
 966    cmd->SetGraphicsRootConstantBufferView( 0, uiRes.uiOrthoConstantBuffer->GetGPUVirtualAddress());
 967
 968    UIDrawContext ctx;
 969    ctx.vertexPtr = reinterpret_cast<UIVertex*>(uiRes.pVertexDataBegin);
 970    ctx.indexPtr = reinterpret_cast<uint16_t*>(uiRes.pIndexDataBegin);
 971    ctx.vertexCount = 0;
 972    ctx.indexCount = 0;
 973    if (!topRibbonLayout.isValid || topRibbonLayout.dpiX != monitorDPIX || topRibbonLayout.dpiY != monitorDPIY) {
 974        PrecomputeTopRibbonLayout(topRibbonLayout, monitorDPIX, monitorDPIY);
 975    }
 976    if (input.mouseWheelDelta != 0 && input.mouseY >= 0.0f && input.mouseY < topRibbonLayout.topUITotalHeightPx) {
 977        const float wheelSteps = input.mouseWheelDelta / (float)WHEEL_DELTA;
 978        const float scrollStepPx = std::max(topRibbonLayout.buttonWidthPx * 2.0f, 120.0f);
 979        topRibbonLayout.scrollOffsetPx -= wheelSteps * scrollStepPx;
 980    }
 981    ClampTopRibbonScroll(topRibbonLayout, W);
 982
 983    float pixelsPerMMx = monitorDPIX / 25.4f;
 984    float pixelsPerMMy = monitorDPIY / 25.4f;
 985    float iconSizePx = topRibbonLayout.iconSizePx;
 986    float buttonHeightPx = topRibbonLayout.buttonHeightPx;
 987	float iconReservedWidthPx = topRibbonLayout.iconReservedWidthPx;
 988    float textStartOffsetPx = topRibbonLayout.textStartOffsetPx;
 989    float textEndInsetPx = topRibbonLayout.textEndInsetPx;
 990    float tabBarHeightPx = topRibbonLayout.tabBarHeightPx;
 991    float topUITotalHeightPx = topRibbonLayout.topUITotalHeightPx;
 992    float roundedCornerRadiusPx = topRibbonLayout.roundedCornerRadiusPx;
 993
 994    auto canPushRect = [&]() {
 995        return ctx.vertexCount + 4 <= uiRes.maxVertices &&
 996            ctx.indexCount + 6 <= uiRes.maxIndices;
 997    };
 998
 999    auto incrementVertexIndexCounters = [&]() {
1000        ctx.vertexPtr += 4;
1001        ctx.indexPtr += 6;
1002        ctx.vertexCount += 4;
1003        ctx.indexCount += 6;
1004    };
1005
1006    auto pushRect = [&](float x, float y, float w, float h, uint32_t color) {
1007        bool pushed = canPushRect();
1008        PushRect(ctx, x, y, w, h, color, uiRes);
1009        if (pushed) incrementVertexIndexCounters();
1010    };
1011
1012    const float uiTextScale = topRibbonLayout.uiTextScale;
1013
1014    auto pushTextClipped = [&](float x, float y, const char32_t* text, float maxWidth, uint32_t color,
1015        float scale) {
1016        if (!text || maxWidth <= 0.0f) return;
1017
1018        float cursorX = x;
1019        float textRight = x + maxWidth;
1020
1021        for (const char32_t* p = text; *p; ++p) {
1022            if (*p > 0x7F) continue;
1023
1024            auto glyphIt = glyphLookup.find(*p);
1025            if (glyphIt == glyphLookup.end()) continue;
1026
1027            const Glyph& g = glyphIt->second;
1028            if (g.width <= 0 || g.height <= 0) {
1029                cursorX += (float)g.advanceX * scale;
1030                continue;
1031            }
1032
1033            // It is always better to be aligned to pixels for better text clarity.
1034            float xpos = std::floor(cursorX + (float)g.bearingX * scale + 0.5f);
1035            float ypos = std::floor(y - (float)g.bearingY * scale + 0.5f);
1036            float glyphWidth = (float)g.width * scale;
1037            float glyphHeight = (float)g.height * scale;
1038            float glyphRight = xpos + glyphWidth;
1039
1040            if (glyphRight > textRight) break;
1041            if (ctx.vertexCount + 4 > uiRes.maxVertices) return;
1042            if (ctx.indexCount + 6 > uiRes.maxIndices) return;
1043
1044            uint16_t base = ctx.vertexCount;
1045            ctx.vertexPtr[0] = { xpos,                  ypos,                   g.uvMinX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
1046            ctx.vertexPtr[1] = { xpos + glyphWidth,     ypos,                   g.uvMaxX, g.uvMinY, color, UI_ENGLISH_ATLAS_SLOT };
1047            ctx.vertexPtr[2] = { xpos + glyphWidth,     ypos + glyphHeight,     g.uvMaxX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
1048            ctx.vertexPtr[3] = { xpos,                  ypos + glyphHeight,     g.uvMinX, g.uvMaxY, color, UI_ENGLISH_ATLAS_SLOT };
1049
1050            ctx.indexPtr[0] = base + 0;
1051            ctx.indexPtr[1] = base + 1;
1052            ctx.indexPtr[2] = base + 2;
1053            ctx.indexPtr[3] = base + 0;
1054            ctx.indexPtr[4] = base + 2;
1055            ctx.indexPtr[5] = base + 3;
1056
1057            ctx.vertexPtr += 4;
1058            ctx.indexPtr += 6;
1059            ctx.vertexCount += 4;
1060            ctx.indexCount += 6;
1061            cursorX += (float)g.advanceX * scale;
1062        }
1063    };
1064
1065    auto textBaselineY = [&](float y, float h, float scale) {
1066        auto glyphIt = glyphLookup.find(U'M');
1067        if (glyphIt == glyphLookup.end()) return y + h * 0.7f;
1068
1069        const Glyph& g = glyphIt->second;
1070        return y + h * 0.5f + (float)g.bearingY * scale - (float)g.height * scale * 0.5f;
1071    };
1072
1073    // ENGINEERING / PROJECT TABs
1074    // Action ids for engineering thread control (UI -> engineering)
1075    constexpr uint32_t ACTION_ENGINEERING_CLOSE = 0xE0000001u;
1076    constexpr uint32_t ACTION_ENGINEERING_CREATE = 0xE0000002u;
1077
1078    float currentX = 0.0f;
1079
1080    uint16_t tabCount = publishedTabCount.load(std::memory_order_acquire);
1081    uint16_t* tabList = publishedTabIndexes.load(std::memory_order_acquire);
1082    
1083    if (canPushRect()) {
1084        PushRect(ctx, 0.0f, 0.0f, 5000.0f, topUITotalHeightPx, uiActiveColors.actionGroupBackground, uiRes);//
1085        incrementVertexIndexCounters();
1086    }
1087
1088    if (canPushRect()) {
1089        PushRect(ctx, 0.0f, 0.0f, 5000.0f, tabBarHeightPx, uiActiveColors.tabBackground, uiRes);//
1090        incrementVertexIndexCounters();
1091    }
1092    // We will allow tabs to shrink progressively when too many tabs exist.
1093    // Compute sizing constraints
1094    const float defaultTabWidth = 160.0f; // legacy fixed width in pixels
1095    const float plusButtonWidth = buttonHeightPx; // reserve square area for '+'
1096    const float minTabWidth = std::max(4.0f * pixelsPerMMx, 8.0f); // 4mm minimum as requested, but at least 8px
1097
1098    // Determine how many slots we need to fit: tabs + one slot for '+' button
1099    uint16_t slotsNeeded = tabCount + 1;
1100    float availableForTabs = std::max(0.0f, W - plusButtonWidth);
1101
1102    float tentativeWidth = availableForTabs / (float)slotsNeeded;
1103    float tabWidthPx = defaultTabWidth;
1104    uint16_t visibleTabs = tabCount;
1105
1106    if (tentativeWidth >= defaultTabWidth) {
1107        tabWidthPx = defaultTabWidth;
1108    } else if (tentativeWidth >= minTabWidth) {
1109        tabWidthPx = tentativeWidth;
1110    } else {
1111        // If tentative width is below minimum, we must hide some tabs.
1112        visibleTabs = (uint16_t)std::floor(availableForTabs / minTabWidth);
1113        if (visibleTabs > tabCount) visibleTabs = tabCount;
1114        tabWidthPx = minTabWidth;
1115    }
1116
1117    // Render visible tabs only; hidden tabs are not drawn (will be handled by horizontal scroll in future)
1118    // Gap between tabs: 0.5 mm on either side
1119    float gapPx = 0.5f * pixelsPerMMx;
1120    for (uint16_t i = 0; i < visibleTabs; i++) {
1121        uint16_t tabID = tabList[i];
1122        bool isActive = (window.activeTabIndex == tabID);
1123
1124        // area for this tab (slot)
1125        float tabX = currentX;
1126        float tabW = tabWidthPx;
1127
1128        // content area inset by half-mm gaps on either side
1129        float contentX = tabX + gapPx;
1130        float contentW = std::max(0.0f, tabW - 2.0f * gapPx);
1131
1132        // X (close) button sizing — square inside tab on the right
1133        float xBtnSize = std::round(std::min(tabW * 0.5f, std::max( (float)std::round(UI_ICON_SIZE_MM * pixelsPerMMx), 10.0f)));
1134        if (xBtnSize + 4.0f > tabW) xBtnSize = std::max(4.0f, tabW - 4.0f);
1135        float xBtnX = tabX + tabW - xBtnSize - 4.0f;
1136        float xBtnY = std::floor((tabBarHeightPx - xBtnSize) * 0.5f);
1137
1138        // Entire tab background — draw only inside content area leaving gaps between tabs
1139        if (isActive) {
1140            PushTopRoundedRectangle(ctx, contentX, 0.0f, contentW, tabBarHeightPx, roundedCornerRadiusPx, uiActiveColors.actionGroupBackground, uiRes);
1141        } else {
1142            bool pushed = canPushRect();
1143            PushRect(ctx, contentX, 0.0f, contentW, tabBarHeightPx, uiActiveColors.tabBackground, uiRes);
1144            if (pushed) incrementVertexIndexCounters();
1145        }
1146
1147        // Check clicks on the X button first to avoid activating the tab when user intends to close
1148        bool xHovered = input.mouseX >= xBtnX && input.mouseX < xBtnX + xBtnSize &&
1149            input.mouseY >= xBtnY && input.mouseY < xBtnY + xBtnSize;
1150        if (xHovered && input.leftButtonPressedThisFrame) {
1151            // Signal close intent to engineering thread. Pass tabID in parameter p1.
1152            PushUIAction(ACTION_ENGINEERING_CLOSE, (uint32_t)tabID, 0);
1153        }
1154
1155        // If user clicked on non-X area of tab, activate it
1156        bool tabHovered = input.mouseX >= tabX && input.mouseX < tabX + tabW &&
1157            input.mouseY >= 0 && input.mouseY < tabBarHeightPx;
1158        if (!xHovered && tabHovered && input.leftButtonPressedThisFrame) {
1159            window.activeTabIndex = tabID; // Render thread will draw this tab's geometry on the next frame.
1160        }
1161
1162        // Draw the X button: only draw rounded background when hovered, otherwise render as plain text
1163        char32_t xChar[2] = { U'x', U'\0' };
1164        if (xHovered) {
1165            PushRoundedRectangle(ctx, xBtnX, xBtnY, xBtnSize, xBtnSize, std::max(1.0f, roundedCornerRadiusPx * 0.6f),
1166                0xFF444444, uiRes);
1167            pushTextClipped(xBtnX + 2.0f, textBaselineY(xBtnY, xBtnSize, uiTextScale), xChar, xBtnSize - 4.0f, 0xFFFFFFFF, uiTextScale);
1168        } else {
1169            // Render as plain small text matching tab text color
1170            pushTextClipped(xBtnX + 2.0f, textBaselineY(xBtnY, xBtnSize, uiTextScale), xChar, xBtnSize - 4.0f, uiActiveColors.tabBackgroundText, uiTextScale);
1171        }
1172
1173        // Draw label clipped to remaining area (avoid overlapping with X)
1174        std::u32string tabLabel;
1175        tabLabel.reserve(allTabs[tabID].fileName.size());
1176        for (wchar_t ch : allTabs[tabID].fileName) {
1177            if (ch <= 0x7F) tabLabel.push_back(static_cast<char32_t>(ch));
1178        }
1179
1180        float labelMaxWidth = contentW - (8.0f + xBtnSize + 4.0f);
1181        pushTextClipped(contentX + 8.0f, textBaselineY(0.0f, tabBarHeightPx, uiTextScale), tabLabel.c_str(), labelMaxWidth, uiActiveColors.tabBackgroundText, uiTextScale);
1182
1183        currentX += tabW;
1184
1185        // Draw 1px vertical separator centered in the gap between tabs (only between tabs)
1186        if (i + 1 < visibleTabs) {
1187            float sepX = tabX + tabW; // center of gap between this tab and next
1188            // align to pixel for crispness
1189            float sepXi = std::floor(sepX + 0.5f);
1190            pushRect(sepXi, 2.0f, 1.0f, tabBarHeightPx - 4.0f, uiActiveColors.actionGroupSeperator);
1191        }
1192    }
1193
1194    // If some tabs are hidden, we may draw a subtle indicator (ellipsis) — skip for now
1195
1196    // Render '+' create new thread button at the end of tab bar
1197    float plusX = currentX + 6.0f; // small padding before plus
1198    float plusSize = std::max(plusButtonWidth, std::round(UI_ICON_SIZE_MM * pixelsPerMMy) + 8.0f);
1199    bool plusHovered = input.mouseX >= plusX && input.mouseX < plusX + plusSize &&
1200        input.mouseY >= (tabBarHeightPx - plusSize) * 0.5f && input.mouseY < (tabBarHeightPx - plusSize) * 0.5f + plusSize;
1201    // '+' button: show rounded background only on hover; otherwise render as plain icon/text
1202    if (plusHovered) {
1203        PushRoundedRectangle(ctx, plusX, (tabBarHeightPx - plusSize) * 0.5f, plusSize, plusSize, roundedCornerRadiusPx,
1204            0xFF444444, uiRes);
1205        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1206            PushIcon(ctx, plusX + (plusSize - iconSizePx) * 0.5f, (tabBarHeightPx - iconSizePx) * 0.5f,
1207                iconSizePx, iconSizePx, gIconAtlasMetadata.mixedIconCodepoints[0], 0xFFFFFFFF, uiRes);
1208        }
1209    } else {
1210        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1211            PushIcon(ctx, plusX + (plusSize - iconSizePx) * 0.5f, (tabBarHeightPx - iconSizePx) * 0.5f,
1212                iconSizePx, iconSizePx, gIconAtlasMetadata.mixedIconCodepoints[0], uiActiveColors.tabBackgroundText, uiRes);
1213        }
1214    }
1215    if (plusHovered && input.leftButtonPressedThisFrame) {
1216        PushUIAction(ACTION_ENGINEERING_CREATE, 0, 0);
1217    }
1218
1219    // TOP BUTTONS (ACTION GROUP BAR)
1220    const float buttonGap = topRibbonLayout.buttonGapPx;
1221    const float actionGroupLabelY = topRibbonLayout.actionGroupLabelY;
1222	const float groupLabelHeight = topRibbonLayout.actionGroupLabelHeightPx;
1223    const float topActionGroupY = topRibbonLayout.topActionGroupY;
1224    const float actionSubGroupLabelY = topRibbonLayout.actionSubGroupLabelY;
1225    const float ribbonScrollX = topRibbonLayout.scrollOffsetPx;
1226
1227    // Draw the 5-pixel high extent-of-ribbon-visible visualization bar in the 5px gap below Action Group labels.
1228    // The gap starts at topActionGroupY - 5.0f.
1229    float extentX = MapRibbonToNav(topRibbonLayout.scrollOffsetPx, topRibbonLayout);
1230    float extentRight = MapRibbonToNav(topRibbonLayout.scrollOffsetPx + W, topRibbonLayout);
1231    float extentW = std::max(1.0f, extentRight - extentX);
1232    // Draw active indicator (orange)
1233    pushRect(extentX, topActionGroupY - 5.0f, extentW, 5.0f, 0xFF3399FF);
1234
1235    for (size_t groupIndex = 0; groupIndex < TotalTopUIActionGroups; ++groupIndex) {
1236        const UIActionGroupNames& group = topUIActionGroupNames[groupIndex];
1237        const UITopRibbonActionGroupLayout& groupLayout = topRibbonLayout.actionGroups[groupIndex];
1238        const char32_t* label = LocalizedUIString(group.labelStringID);
1239        const bool hovered = group.isEnabled &&
1240            input.mouseX >= groupLayout.navX && input.mouseX < groupLayout.navX + groupLayout.navWidth &&
1241            input.mouseY >= actionGroupLabelY && input.mouseY < actionGroupLabelY + groupLabelHeight;
1242
1243        if (hovered) {
1244            pushRect(groupLayout.navX, actionGroupLabelY, groupLayout.navWidth, groupLabelHeight,
1245                uiActiveColors.tabBackgroundHover);
1246        }
1247        if (hovered && input.leftButtonPressedThisFrame) {
1248            topRibbonLayout.scrollOffsetPx = groupLayout.contentStartX;
1249            ClampTopRibbonScroll(topRibbonLayout, W);
1250        }
1251
1252        pushTextClipped(groupLayout.navX + 4.0f, textBaselineY(actionGroupLabelY, groupLabelHeight, uiTextScale),
1253            label, groupLayout.navWidth - 8.0f, uiActiveColors.actionText, uiTextScale);
1254    }
1255
1256    for (size_t runIndex = 0; runIndex < topRibbonLayout.actionSubGroupRunCount; ++runIndex) {
1257        const UITopRibbonSubGroupRunLayout& run = topRibbonLayout.actionSubGroupRuns[runIndex];
1258        if (run.subGroupIndex >= TotalTopUIActionSubGroups) continue;
1259
1260        const UIActionGroupNames& subGroup = topUIActionSubGroupNames[run.subGroupIndex];
1261        const char32_t* label = LocalizedUIString(subGroup.labelStringID);
1262        const float runX = run.contentStartX - ribbonScrollX;
1263        const float runWidth = std::max(0.0f, run.contentEndX - run.contentStartX);
1264        const float labelWidth = MeasureUIStringWidth(label, uiTextScale);
1265        const float labelX = runX + std::max(4.0f, (runWidth - labelWidth) * 0.5f);
1266
1267        pushTextClipped(labelX, textBaselineY(actionSubGroupLabelY, groupLabelHeight, uiTextScale),
1268            label, std::max(0.0f, runWidth - 8.0f), uiActiveColors.actionText, uiTextScale);
1269
1270        if (runIndex + 1 < topRibbonLayout.actionSubGroupRunCount) {
1271            const float lineX = std::floor(run.contentEndX + buttonGap * 0.5f - ribbonScrollX);
1272            const float lineHeight = 3.0f * topRibbonLayout.buttonHeightPx + 2.0f;
1273            if (lineX >= -1.0f && lineX <= W + 1.0f) {
1274                pushRect(lineX, topActionGroupY, 1.0f, lineHeight, 0xFF555555);
1275            }
1276        }
1277    }
1278
1279    for (size_t i = 0; i < TotalUIControls; ++i) {
1280        const auto& ctrl = AllUIControls[i];
1281        const UITopRibbonControlLayout& ctrlLayout = topRibbonLayout.controls[i];
1282        const float btnX = ctrlLayout.x - ribbonScrollX;
1283        const float btnY = ctrlLayout.y;
1284        const float btnWidth = ctrlLayout.width;
1285        const float btnHeight = ctrlLayout.height;
1286        const char32_t* label = LocalizedControlLabel(ctrl);
1287        uint32_t baseColor = StableRandomUIColour((uint32_t)ctrl.action ^ ((uint32_t)i * 0x9E3779B9u));// Render
1288        uint32_t iconColor = StableRandomUIColour(((uint32_t)ctrl.action << 1) ^ 0xA511E9B3u ^ (uint32_t)i);
1289        const bool controlVisible = btnX + btnWidth >= 0.0f && btnX <= W;
1290        bool hovered = false;
1291
1292        if (ctrl.type == 1 || ctrl.type == 2) {                     // Button or Dropdown trigger
1293            hovered = controlVisible && ctrl.isEnabled && (input.mouseX >= btnX && input.mouseX < btnX + btnWidth &&
1294                input.mouseY >= btnY && input.mouseY < btnY + btnHeight);
1295            uint32_t drawColor = hovered && input.leftButtonDown ? 0xFF333333 : baseColor;
1296            if (hovered && !input.leftButtonDown) drawColor = 0xFF555555;
1297            if (controlVisible) {
1298                if (hovered) {
1299                    PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1300                        drawColor, uiRes);
1301                } else {
1302                    float highlightWidth = ctrl.showText ? iconReservedWidthPx : btnWidth;
1303                    PushRoundedRectangle(ctx, btnX, btnY, highlightWidth, btnHeight, roundedCornerRadiusPx,
1304                        baseColor, uiRes);
1305                }
1306            }
1307
1308            bool clicked = hovered && input.leftButtonPressedThisFrame;
1309
1310            if (clicked && ctrl.isEnabled) {
1311                PushUIAction((uint32_t)ctrl.action);
1312                if (ctrl.zIndex == 1) { // Dropdown trigger
1313                    window.activeDropdownAction = ctrl.action;
1314                }
1315            }
1316
1317            if (!ctrl.isEnabled) { // Gray-out overlay for disabled controls
1318                if (controlVisible) {
1319                    float highlightWidth = ctrl.showText ? iconReservedWidthPx : btnWidth;
1320                    PushRoundedRectangle(ctx, btnX, btnY, highlightWidth, btnHeight, roundedCornerRadiusPx,
1321                        0xAA333333, uiRes);
1322                }
1323            }
1324        }
1325        else if (ctrl.type == 3) {
1326            // Future textbox
1327            if (controlVisible) {
1328                PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1329                    0xFF1E1E1E, uiRes);
1330            }
1331        }
1332        else {
1333            // Plain label
1334            if (controlVisible) {
1335                PushRoundedRectangle(ctx, btnX, btnY, btnWidth, btnHeight, roundedCornerRadiusPx,
1336                    0xFF2D2D30, uiRes);
1337            }
1338        }
1339
1340        if (!controlVisible) continue;
1341
1342        float iconX = btnX + (iconReservedWidthPx - iconSizePx) * 0.5f;
1343        float iconY = btnY + (btnHeight - iconSizePx) * 0.5f;
1344        if (!gIconAtlasMetadata.mixedIconCodepoints.empty()) {
1345            const uint32_t randomIconIndex =
1346                ((uint32_t)ctrl.action ^ (uint32_t)i) % (uint32_t)gIconAtlasMetadata.mixedIconCodepoints.size();
1347            PushIcon(ctx, iconX, iconY, iconSizePx, iconSizePx,
1348                gIconAtlasMetadata.mixedIconCodepoints[randomIconIndex], iconColor, uiRes);
1349        }
1350
1351        if (ctrl.showText) {
1352            float textX = btnX + textStartOffsetPx;
1353            float textWidth = btnWidth - textStartOffsetPx - textEndInsetPx;
1354            uint32_t textColor = 0xFFFFFFFF; // default hovered/active color (white)
1355            if (!hovered) {
1356                textColor = ctrl.isEnabled ? uiActiveColors.actionText : 0xAA888888;
1357            }
1358            pushTextClipped(textX, textBaselineY(btnY, btnHeight, uiTextScale),
1359                label, textWidth, textColor, uiTextScale);
1360        }
1361    }
1362
1363    const int activeTabIndex = window.activeTabIndex;
1364    if (activeTabIndex >= 0 && activeTabIndex < MV_MAX_TABS) {
1365        DATASETTAB& tab = allTabs[activeTabIndex];
1366        const DataTreeView::StateSnapshot dataTreeState = DataTreeView::Snapshot(tab.dataTreeView);
1367
1368        if (dataTreeState.isVisible) {
1369            std::vector<DataTreeView::Node> treeNodes;
1370            std::vector<uint64_t> expandedNodeIds;
1371            if (tab.storageObjectsMutex) {
1372                std::lock_guard<std::mutex> lock(*tab.storageObjectsMutex);
1373                treeNodes.reserve(tab.storageLogicalObjects.size() + tab.storageObjects3D.size());
1374                expandedNodeIds = tab.expandedDataTreeNodeIds;
1375
1376                for (const StoredLogicalObject& object : tab.storageLogicalObjects) {
1377                    if (!object.object) continue;
1378                    DataTreeView::Node node;
1379                    node.objectId = object.object->memoryID;
1380                    node.parentObjectId = object.object->memoryIDParent;
1381                    node.label = BuildTreeNodeLabel(object.objectType, object.object, object.memoryId);
1382                    treeNodes.push_back(std::move(node));
1383                }
1384
1385                for (const StoredGeometryObject3D& object : tab.storageObjects3D) {
1386                    if (!object.object) continue;
1387                    DataTreeView::Node node;
1388                    node.objectId = object.object->memoryID;
1389                    node.parentObjectId = object.object->memoryIDParent;
1390                    node.label = BuildTreeNodeLabel(object.objectType, object.object, object.memoryId);
1391                    treeNodes.push_back(std::move(node));
1392                }
1393            }
1394
1395            DataTreeView::BuildRequest treeRequest;
1396            treeRequest.tabName = tab.fileName;
1397            treeRequest.nodes = &treeNodes;
1398            treeRequest.expandedNodeIds = &expandedNodeIds;
1399            treeRequest.viewportTopPx = topUITotalHeightPx;
1400            treeRequest.viewportHeightPx = std::max(0.0f, H - topUITotalHeightPx);
1401            treeRequest.pixelsPerMMX = pixelsPerMMx;
1402            treeRequest.pixelsPerMMY = pixelsPerMMy;
1403
1404            const std::vector<DataTreeView::Row> treeRows =
1405                DataTreeView::BuildRows(treeRequest, dataTreeState);
1406            uint64_t hoveredToggleObjectId = 0;
1407            const bool toggleHovered =
1408                DataTreeView::HitTestToggle(treeRows, input.mouseX, input.mouseY, hoveredToggleObjectId);
1409
1410            if (toggleHovered && input.leftButtonPressedThisFrame) {
1411                if (hoveredToggleObjectId == 0) {
1412                    PushUIAction(DataTreeView::kToggleEverythingUIAction, static_cast<uint32_t>(activeTabIndex), 0);
1413                } else {
1414                    PushUIAction(DataTreeView::kToggleNodeUIAction,
1415                        static_cast<uint32_t>(activeTabIndex), hoveredToggleObjectId);
1416                }
1417            }
1418
1419            for (const DataTreeView::Row& row : treeRows) {
1420                const bool rowToggleHovered = toggleHovered && row.objectId == hoveredToggleObjectId;
1421                const uint32_t rowColor = rowToggleHovered ? 0xFF3399FF : 0xFFFFFFFF;
1422                const float baselineY = textBaselineY(row.y, row.height, uiTextScale);
1423
1424                if (row.hasToggle) {
1425                    const char32_t toggleText[2] = { row.isExpanded ? U'-' : U'+', U'\0' };
1426                    pushTextClipped(row.toggleX, baselineY, toggleText, row.toggleWidth, rowColor, uiTextScale);
1427                }
1428
1429                pushTextClipped(row.textX, baselineY, row.label.c_str(), row.textMaxWidth, rowColor, uiTextScale);
1430            }
1431        }
1432    }
1433
1434    // ACTIVE DROPDOWN (placeholder)
1435    if (window.activeDropdownAction != Commands::INVALID) {
1436        float dropX = 400.0f;   // TODO: track real button X for proper positioning
1437        float dropY = topActionGroupY + 80.0f;
1438        pushRect(dropX, dropY, 160, 220, 0xFF1E1E1E);
1439        window.activeDropdownAction = Commands::INVALID;   // immediate-mode auto-close
1440    }
1441
1442    // DRAW ALL UI GEOMETRY
1443    if (ctx.indexCount == 0) return;
1444
1445    D3D12_VERTEX_BUFFER_VIEW vbv{};
1446    vbv.BufferLocation = uiRes.uiVertexBuffer->GetGPUVirtualAddress();
1447    vbv.SizeInBytes = ctx.vertexCount * sizeof(UIVertex);
1448    vbv.StrideInBytes = sizeof(UIVertex);
1449
1450    D3D12_INDEX_BUFFER_VIEW ibv{};
1451    ibv.BufferLocation = uiRes.uiIndexBuffer->GetGPUVirtualAddress();
1452    ibv.SizeInBytes = ctx.indexCount * sizeof(uint16_t);
1453    ibv.Format = DXGI_FORMAT_R16_UINT;
1454
1455    cmd->IASetVertexBuffers(0, 1, &vbv);
1456    cmd->IASetIndexBuffer(&ibv);
1457    cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1458    cmd->DrawIndexedInstanced(ctx.indexCount, 1, 0, 0, 0);
1459}

Miscellaneous philosophy:

Renderer must support these scripts:

Script Languages
Latin English, German, French, Spanish, Portuguese, Polish, Dutch, Swedish, Italian
Cyrillic Russian, Ukrainian
CJK Chinese, Japanese
Hangul Korean
Arabic Urdu
Indic Hindi, Bengali, Telugu, Tamil, etc
Thai Thai
Vietnamese Latin + diacritics

Recommended Font Families: NotoSans-Regular NotoSansCJK-Regular NotoSansDevanagari NotoSansTamil NotoSansTelugu NotoSansThai NotoSansArabic NotoSansHebrew