Release v0.6.12: improve continuous correction and resize log viewer

This commit is contained in:
2026-10-30 00:15:09 +02:00
parent c326db7dc5
commit 77f27ea087
17 changed files with 220 additions and 99 deletions
+56 -46
View File
@@ -34,6 +34,7 @@ struct WindowLayout
bool scrolling{}, arranging{}, history{};
int wheelRemainder{}, horizontalWheelRemainder{};
std::vector<std::pair<HWND, RECT>> controls; // Original 96-DPI geometry; never rescale rounded pixels.
HWND stretch{}, bottomLeft{}, bottomRight{};
};
WindowLayout *windowLayoutState(HWND w)
{
@@ -69,8 +70,9 @@ void arrangeWindow(HWND w)
layout->y = SetScrollInfo(w, SB_VERT, &vertical, TRUE);
}
RECT client{}; GetClientRect(w, &client);
int extraWidth = layout->history ? std::max(0, int(client.right) - scale(w, layout->width)) : 0;
int extraHeight = layout->history ? std::max(0, int(client.bottom) - scale(w, layout->height)) : 0;
bool expandable = layout->history || layout->stretch;
int extraWidth = expandable ? std::max(0, int(client.right) - scale(w, layout->width)) : 0;
int extraHeight = expandable ? std::max(0, int(client.bottom) - scale(w, layout->height)) : 0;
struct Move { HWND child; RECT rect; };
std::vector<Move> moves;
for (const auto &[child, r] : layout->controls)
@@ -80,6 +82,13 @@ void arrangeWindow(HWND w)
scale(w, r.top) - layout->y + (id >= 110 && id <= 114 ? extraHeight : 0)};
next.right = next.left + scale(w, r.right - r.left) + (id == 101 || id == 102 ? extraWidth : 0);
next.bottom = next.top + scale(w, r.bottom - r.top) + (id == 102 ? extraHeight : 0);
if (child == layout->stretch)
{
next.right += extraWidth;
next.bottom += extraHeight;
}
if (child == layout->bottomLeft || child == layout->bottomRight)
OffsetRect(&next, child == layout->bottomRight ? extraWidth : 0, extraHeight);
RECT current{}; GetWindowRect(child, &current);
MapWindowPoints(nullptr, w, reinterpret_cast<POINT *>(&current), 2);
if (!EqualRect(&current, &next)) moves.push_back({child, next});
@@ -1530,53 +1539,41 @@ void showHistory(App &app, bool visible)
SetFocus(GetDlgItem(app.historyWindow, 101));
refreshHistory(app);
}
#endif
#ifndef KS_RESIDENT_APP
namespace
{
constexpr int LogChoice = 601, LogRows = 602, LogRefresh = 603, LogStatus = 604, LogCopy = 605,
LogClear = 606;
LRESULT CALLBACK logListProc(HWND w, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR, DWORD_PTR)
void fitLogDetails(HWND list)
{
// Header notifications go to the ListView, not its top-level parent.
if (msg == WM_NOTIFY && ((NMHDR *)lp)->hwndFrom == ListView_GetHeader(w) &&
((NMHDR *)lp)->code == NM_CUSTOMDRAW)
if (GetPropW(list, L"FittingLogDetails") || Header_GetItemCount(ListView_GetHeader(list)) != 4) return;
SetPropW(list, L"FittingLogDetails", (HANDLE)1);
RECT client{};
GetClientRect(list, &client);
int remaining = client.right;
for (int column = 0; column < 3; ++column)
remaining -= ListView_GetColumnWidth(list, column);
int width = std::max(scale(list, 120), remaining);
if (ListView_GetColumnWidth(list, 3) != width)
ListView_SetColumnWidth(list, 3, width);
RemovePropW(list, L"FittingLogDetails");
}
LRESULT CALLBACK logListResizeProc(HWND w, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR, DWORD_PTR)
{
auto result = DefSubclassProc(w, msg, wp, lp);
if (msg == WM_SIZE)
fitLogDetails(w);
if (msg == WM_NOTIFY && lp)
{
auto *draw = (NMCUSTOMDRAW *)lp;
if (draw->dwDrawStage == CDDS_PREPAINT)
{
FillRect(draw->hdc, &draw->rc, GetSysColorBrush(COLOR_BTNFACE));
return CDRF_NOTIFYITEMDRAW;
}
if (draw->dwDrawStage == CDDS_ITEMPREPAINT)
{
int saved = SaveDC(draw->hdc);
FillRect(draw->hdc, &draw->rc, GetSysColorBrush(COLOR_BTNFACE));
RECT edge = draw->rc;
edge.top = edge.bottom - 1;
FillRect(draw->hdc, &edge, GetSysColorBrush(COLOR_3DSHADOW));
edge = draw->rc;
edge.left = edge.right - 1;
FillRect(draw->hdc, &edge, GetSysColorBrush(COLOR_3DSHADOW));
wchar_t title[128]{};
HDITEMW item{};
item.mask = HDI_TEXT;
item.pszText = title;
item.cchTextMax = 128;
Header_GetItem(draw->hdr.hwndFrom, (int)draw->dwItemSpec, &item);
auto headerFont = (HFONT)SendMessageW(draw->hdr.hwndFrom, WM_GETFONT, 0, 0);
if (headerFont)
SelectObject(draw->hdc, headerFont);
SetBkMode(draw->hdc, TRANSPARENT);
SetTextColor(draw->hdc, GetSysColor(COLOR_BTNTEXT));
RECT text = draw->rc;
InflateRect(&text, -scale(w, 6), 0);
DrawTextW(draw->hdc, title, -1, &text, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX);
RestoreDC(draw->hdc, saved);
return CDRF_SKIPDEFAULT;
}
auto *header = reinterpret_cast<NMHEADERW *>(lp);
if (header->hdr.hwndFrom == ListView_GetHeader(w) &&
(header->hdr.code == HDN_ITEMCHANGEDW || header->hdr.code == HDN_ITEMCHANGEDA) &&
header->iItem >= 0 && header->iItem < 3)
fitLogDetails(w);
}
if (msg == WM_NCDESTROY)
RemoveWindowSubclass(w, logListProc, 1);
return DefSubclassProc(w, msg, wp, lp);
if (msg == WM_NCDESTROY) RemoveWindowSubclass(w, logListResizeProc, 3);
return result;
}
void copyLogRows(HWND w, App &app)
{
@@ -1596,7 +1593,11 @@ void copyLogRows(HWND w, App &app)
copied += L"\r\n";
}
if (!copied.empty())
app.ownSequence = writeClipboard(app.window, textClip(copied));
{
auto clip = textClip(copied);
clip.formats.push_back({0, L"ExcludeClipboardContentFromMonitorProcessing", {1, 0, 0, 0}});
app.ownSequence = writeClipboard(app.window, clip);
}
}
void refreshLogView(HWND w, App &app)
{
@@ -1664,6 +1665,7 @@ void refreshLogView(HWND w, App &app)
InvalidateRect(list, nullptr, TRUE);
if (row)
ListView_EnsureVisible(list, row - 1, FALSE);
fitLogDetails(list);
value(w, LogStatus, app.settings.writeLog ? L"Logging enabled: failed operations only."
: L"Logging disabled. Showing previously saved entries.");
}
@@ -1696,7 +1698,7 @@ LRESULT CALLBACK logProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
{
try
{
clearDiagnosticLog(SendDlgItemMessageW(w, LogChoice, CB_GETCURSEL, 0, 0) == 1);
app->clearLog(SendDlgItemMessageW(w, LogChoice, CB_GETCURSEL, 0, 0) == 1);
refreshLogView(w, *app);
}
catch (const std::exception &e) { error(w, e); }
@@ -1758,7 +1760,7 @@ void showLogs(App &app)
auto list = control(w, WC_LISTVIEWW, L"", WS_TABSTOP | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS,
LogRows, 16, 82, 868, 454);
ListView_SetExtendedListViewStyle(list, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
SetWindowSubclass(list, logListProc, 1, 0);
SetWindowSubclass(list, logListResizeProc, 3, 0);
const wchar_t *titles[] = {L"Time", L"Attempt", L"Event", L"Details"};
const int widths[] = {155, 65, 195, 900};
for (int i = 0; i < 4; ++i)
@@ -1769,12 +1771,20 @@ void showLogs(App &app)
column.cx = scale(w, widths[i]);
ListView_InsertColumn(list, i, &column);
}
label(w, L"Select rows to copy. Ctrl+A selects all; Ctrl+C copies selected rows.", 16, 551, 720);
auto hint = control(w, L"STATIC", L"Select rows to copy. Ctrl+A selects all; Ctrl+C copies selected rows.",
0, 0, 16, 551, 720, 22);
button(w, L"Close", IDCANCEL, 774, 548, 110);
auto *layout = windowLayoutState(w);
layout->stretch = list;
layout->bottomLeft = hint;
layout->bottomRight = GetDlgItem(w, IDCANCEL);
arrangeWindow(w);
refreshLogView(w, app);
showRequestedWindow(w);
activateWindow(w);
}
#else
void showLogs(App &app) { launchConfiguration(app, false, true); }
#endif
#ifndef KS_RESIDENT_APP
void showSettings(App &app, bool visible)