Files
win-dictate/UI-and-progress-bar-rebuild-part-2.md
T

4.0 KiB

Win Dictation — Fix Note 01

Every control renders as the blue Record pill

Applies to: the single-surface UI rewrite described in UI-and-Progress-Rebuild.md (Part 1), after the first build. Status: root cause confirmed, one-line fix below.


Symptom

After wiring up the single-surface paint, every control — Record, Pin, the mic and model selects, and Copy / Paste / Clear — renders as the same blue accent pill with a white dot. No labels, no distinct styles. (The window chrome, card, and scrollbar are correct; only the widgets are wrong.)

The mistake

Widget::kind is declared without an initializer, and the widget array has static storage duration, so the whole array is zero-initialized:

struct Widget {
    WK     kind;        // <-- no initializer
    RectF  r;
    bool   hover = false;
    bool   pressed = false;
    float  anim = 0.0f;
};
static Widget g_w[ (int)WK::Transcript + 1 ];   // zero-filled → every kind == 0

WK::RecordHero is the first enumerator, i.e. value 0. So every slot's kind reads as RecordHero. LayoutWidgets() assigns each slot's rectangle (g_w[(int)WK::Pin].r = …, etc.) but never its kind. The paint loop dispatches on kind:

for (auto& w : g_w) {
    switch (w.kind) {                 // 0 for every widget
        case WK::RecordHero: DrawHero(g, w);          break;   // <-- all eight land here
        case WK::Copy:       DrawGhost(g, w, L"Copy");  break; // unreachable
        case WK::SelAudio:   DrawSelectSurface(...);    break; // unreachable
        ...
    }
}

Result: DrawHero paints all eight widgets → eight identical blue pills.

The required change

The array is indexed by enum value (g_w[(int)WK::Pin], g_w[(int)WK::Copy], …), so slot i must carry kind == (WK)i. Stamp the kinds once, at the top of LayoutWidgets() — it runs before the first paint and again on every resize / DPI change, so the invariant is always re-asserted:

void LayoutWidgets(int W, int H) {
    for (int i = 0; i < (int)std::size(g_w); ++i)   // <-- ADD THESE TWO LINES
        g_w[i].kind = (WK)i;                         //     slot index == kind

    for (auto& w : g_w) w.r = RectF(0, 0, 0, 0);
    float s = g_dpiScale;
    // ... unchanged ...
}

Equivalent alternative: an InitWidgets() helper called once in wWinMain before the first LayoutWidgets/paint. Doing it inside LayoutWidgets is the most robust, because nothing can paint before layout has run.

Invariant to preserve: g_w[i].kind == (WK)i. As long as rectangles keep being assigned via g_w[(int)WK::X].r, the slot index always equals the enum value and this holds.

After this change, DrawGhost (Copy/Paste/Clear), DrawPinSurface, and DrawSelectSurface (mic/model) take over their slots, and you get the intended distinct, borderless, label-bearing controls.

Secondary issue noticed (cosmetic — not fixed here)

The transcript placeholder ("Your transcription will appear here…") won't appear. PaintSurface draws it into the transcript rect, but the real multiline EDIT child window covers that rect and is opaque, so it hides the parent-painted text. EM_SETCUEBANNER only works on single-line edits. Clean fix: subclass the multiline EDIT and draw the cue in its own WM_PAINT when the control is empty and unfocused. Tracked as a separate follow-up.

Verify after rebuilding

  1. Top row: a wide blue Record pill + a quiet Pin toggle (accent only when pinned).
  2. Bottom: mic and model selects (label + chevron, faint at rest) and quiet Copy / Paste / Clear ghost buttons (text only at rest).
  3. Hover a ghost button → a soft fill fades in; press → slightly darker. No resting borders, no hairlines.
  4. Independent of this fix, run a transcription and confirm the progress % rises smoothly and the "… s left" value counts down (not up).

Companion to UI-and-Progress-Rebuild.md. This note documents a correction to that guide's Part 1 scaffold; the guide itself is left unchanged.