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

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