Docs refresh: add user manual & architecture review; update README with accurate details

This commit is contained in:
Win Dictation Dev
2026-06-11 20:52:24 +12:00
parent e04b003bf8
commit a94ba824d6
19 changed files with 1609 additions and 466 deletions
+96 -12
View File
@@ -218,6 +218,8 @@ struct PopupState {
int rowH = 30;
int pad = 3;
int wheelAccum = 0;
bool canDelete = false;
int hotX = -1;
};
static PopupState g_pop;
@@ -659,16 +661,79 @@ static int PopupRowFromY(int y) {
return abs;
}
static Rect PopupDeleteRect(const RECT& rc, int visIdx) {
int n = (int)g_pop.items.size();
bool hasBar = n > g_pop.visRows;
int barW = std::max(3, (int)(4 * g_dpiScale));
int rowW = rc.right - 2 * g_pop.pad - (hasBar ? barW + g_pop.pad : 0);
int xW = g_pop.rowH;
return Rect(g_pop.pad + rowW - xW, g_pop.pad + visIdx * g_pop.rowH, xW, g_pop.rowH - 2);
}
static void PopupRefreshHotFromCursor(HWND h) {
POINT pt; GetCursorPos(&pt); ScreenToClient(h, &pt);
int row = PopupRowFromY(pt.y);
int hx = -1;
if (g_pop.canDelete && row >= 0) {
RECT rc; GetClientRect(h, &rc);
if (PopupDeleteRect(rc, row - g_pop.scroll).Contains(pt.x, pt.y)) hx = row;
}
g_pop.hot = row; g_pop.hotX = hx;
}
static void PopupDeleteRow(HWND h, int row) {
if (row < 0 || row >= (int)g_history.size()) return;
std::wstring path = g_history[row].path;
bool ok = DeleteFileW(path.c_str()) != FALSE;
if (ok && path == g_sessionPath) {
g_sessionPath.clear();
g_lastLoadedText = GetEditText(g_pop.owner);
}
g_history = LoadHistoryIndex();
g_pop.items.clear();
g_pop.items.reserve(g_history.size());
for (const auto& e : g_history) g_pop.items.push_back(e.label);
SetStatus(g_pop.owner, ok ? L"Deleted" : L"Delete failed");
int n = (int)g_pop.items.size();
if (n == 0) { DestroyWindow(h); return; }
if (n < g_pop.visRows) {
g_pop.visRows = n;
int newH = n * g_pop.rowH + 2 * g_pop.pad;
RECT wr; GetWindowRect(h, &wr);
SetWindowPos(h, nullptr, wr.left, wr.bottom - newH,
wr.right - wr.left, newH, SWP_NOZORDER | SWP_NOACTIVATE);
}
int maxScroll = std::max(0, n - g_pop.visRows);
g_pop.scroll = std::min(g_pop.scroll, maxScroll);
PopupRefreshHotFromCursor(h);
InvalidateRect(h, nullptr, FALSE);
}
LRESULT CALLBACK PopupProc(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_MOUSEMOVE: {
int row = PopupRowFromY(GET_Y_LPARAM(l));
if (row != g_pop.hot) { g_pop.hot = row; InvalidateRect(h, nullptr, FALSE); }
int hx = -1;
if (g_pop.canDelete && row >= 0) {
RECT rc; GetClientRect(h, &rc);
if (PopupDeleteRect(rc, row - g_pop.scroll).Contains(GET_X_LPARAM(l), GET_Y_LPARAM(l)))
hx = row;
}
if (row != g_pop.hot || hx != g_pop.hotX) {
g_pop.hot = row; g_pop.hotX = hx;
InvalidateRect(h, nullptr, FALSE);
}
TRACKMOUSEEVENT t{ sizeof(t), TME_LEAVE, h, 0 }; TrackMouseEvent(&t);
return 0;
}
case WM_MOUSELEAVE:
g_pop.hot = -1; InvalidateRect(h, nullptr, FALSE);
g_pop.hot = -1; g_pop.hotX = -1; InvalidateRect(h, nullptr, FALSE);
return 0;
case WM_MOUSEWHEEL: {
int maxScroll = (int)g_pop.items.size() - g_pop.visRows;
@@ -679,17 +744,25 @@ LRESULT CALLBACK PopupProc(HWND h, UINT m, WPARAM w, LPARAM l) {
g_pop.wheelAccum -= steps * WHEEL_DELTA;
g_pop.scroll -= steps * 3;
g_pop.scroll = std::max(0, std::min(g_pop.scroll, maxScroll));
POINT pt; GetCursorPos(&pt); ScreenToClient(h, &pt);
g_pop.hot = PopupRowFromY(pt.y);
PopupRefreshHotFromCursor(h);
InvalidateRect(h, nullptr, FALSE);
}
return 0;
}
case WM_KEYDOWN:
if (w == VK_ESCAPE) DestroyWindow(h);
else if (w == VK_DELETE && g_pop.canDelete && g_pop.hot >= 0)
PopupDeleteRow(h, g_pop.hot);
return 0;
case WM_LBUTTONUP: {
int row = PopupRowFromY(GET_Y_LPARAM(l));
if (g_pop.canDelete && row >= 0) {
RECT rc; GetClientRect(h, &rc);
if (PopupDeleteRect(rc, row - g_pop.scroll).Contains(GET_X_LPARAM(l), GET_Y_LPARAM(l))) {
PopupDeleteRow(h, row);
return 0;
}
}
if (row >= 0)
PostMessageW(g_pop.owner, WM_APP_SELECT, (WPARAM)g_pop.ctrlId, (LPARAM)row);
DestroyWindow(h);
@@ -712,21 +785,31 @@ LRESULT CALLBACK PopupProc(HWND h, UINT m, WPARAM w, LPARAM l) {
Rect all(0, 0, rc.right, rc.bottom);
FillRound(g, T_CARD, all, 10);
StrokeRound(g, T_FAINT, all, 10, 1.0f);
int n = (int)g_pop.items.size();
bool hasBar = n > g_pop.visRows;
int barW = std::max(3, (int)(4 * g_dpiScale));
int rowW = rc.right - 2 * g_pop.pad - (hasBar ? barW + g_pop.pad : 0);
int last = std::min(n, g_pop.scroll + g_pop.visRows);
int n = (int)g_pop.items.size();
bool hasBar = n > g_pop.visRows;
int barW = std::max(3, (int)(4 * g_dpiScale));
int rowW = rc.right - 2 * g_pop.pad - (hasBar ? barW + g_pop.pad : 0);
int reserve = g_pop.canDelete ? g_pop.rowH : 0;
int last = std::min(n, g_pop.scroll + g_pop.visRows);
for (int i = g_pop.scroll; i < last; ++i) {
int vis = i - g_pop.scroll;
Rect row(g_pop.pad, g_pop.pad + vis * g_pop.rowH, rowW, g_pop.rowH - 2);
if (i == g_pop.hot) FillRound(g, T_CARD_HI, row, 7);
RectF tb((REAL)row.X + 9, (REAL)row.Y, (REAL)row.Width - 12, (REAL)row.Height);
RectF tb((REAL)row.X + 9, (REAL)row.Y, (REAL)(row.Width - 12 - reserve), (REAL)row.Height);
DrawTextC(g, g_pop.items[i].c_str(), *g_gpUI,
(i == g_pop.sel) ? T_ACCENT : T_TEXT,
tb, StringAlignmentNear, StringAlignmentCenter);
if (g_pop.canDelete && i == g_pop.hot) {
Rect xr = PopupDeleteRect(rc, vis);
if (i == g_pop.hotX) FillRound(g, T_CARD_LO, xr, 6);
Color xc = (i == g_pop.hotX) ? T_DANGER : T_FAINT;
Pen xpen(xc, 1.8f);
int xpad = std::max(6, (int)(8 * g_dpiScale));
g.DrawLine(&xpen, xr.X + xpad, xr.Y + xpad,
xr.X + xr.Width - xpad, xr.Y + xr.Height - xpad);
g.DrawLine(&xpen, xr.X + xr.Width - xpad, xr.Y + xpad,
xr.X + xpad, xr.Y + xr.Height - xpad);
}
}
if (hasBar) {
int trackH = rc.bottom - 2 * g_pop.pad;
@@ -785,6 +868,7 @@ void ShowSelectPopup(HWND owner, int ctrlId, const std::vector<std::wstring>& it
g_pop.sel = sel;
g_pop.owner = owner;
g_pop.ctrlId = ctrlId;
g_pop.canDelete = (ctrlId == ID_SEL_HISTORY);
g_pop.visRows = visRows;
g_pop.rowH = rowH;
g_pop.pad = pad;