Release v0.6.38: avoid unnecessary RichEdit and cancel hidden history previews

This commit is contained in:
2026-11-04 17:24:57 +02:00
parent 30e70192dc
commit fa2f6d2ce4
12 changed files with 62 additions and 30 deletions
+2 -1
View File
@@ -252,7 +252,7 @@ DWORD CALLBACK readStream(DWORD_PTR cookie, LPBYTE buffer, LONG size, LONG *read
return 0;
}
} // namespace
std::string richPreview(const Clip &clip)
std::string richPreview(const Clip &clip, bool plainFallback)
{
for (const auto &f : clip.formats)
if (f.name == L"Rich Text Format" && f.data.size() <= 65536)
@@ -276,6 +276,7 @@ std::string richPreview(const Clip &clip)
{
}
}
if (!plainFallback) return {};
auto text = clip.text();
if (text.size() > 4096)
text.resize(4096);
+1 -1
View File
@@ -2,7 +2,7 @@
#include "platform.h"
namespace ks
{
std::string richPreview(const Clip &);
std::string richPreview(const Clip &, bool plainFallback = true);
HWND createRichText(HWND parent, int id, bool visible);
bool setRichText(HWND, const std::string &);
} // namespace ks
+34 -14
View File
@@ -468,12 +468,17 @@ struct SearchResult
uint64_t generation;
std::vector<Entry> items;
};
struct TextPreview
{
std::string rtf;
std::wstring plain;
};
struct PreviewResult
{
uint64_t generation;
std::wstring id;
Pixels pixels;
std::string rtf;
TextPreview text;
};
struct HistoryView
{
@@ -481,8 +486,9 @@ struct HistoryView
std::vector<Entry> items;
std::wstring rememberedSelection, rememberedTop;
uint64_t generation{};
std::shared_ptr<std::atomic<bool>> previewActive = std::make_shared<std::atomic<bool>>(true);
std::map<std::wstring, Pixels> thumbnails;
std::map<std::wstring, std::string> richTexts;
std::map<std::wstring, TextPreview> richTexts;
HWND rich{};
std::set<std::wstring> pending;
bool scrolling{};
@@ -504,6 +510,8 @@ std::wstring selected(HWND w, HistoryView *v)
}
void search(HWND w, HistoryView *v)
{
v->previewActive->store(false);
v->previewActive = std::make_shared<std::atomic<bool>>(true);
v->closed = false;
auto q = value(w, 101);
auto generation = ++v->generation;
@@ -532,23 +540,31 @@ void requestPreview(HWND w, HistoryView *v, const Entry &entry)
if (v->thumbnails.contains(id) || v->richTexts.contains(id) || !v->pending.insert(id).second)
return;
auto generation = v->generation;
auto active = v->previewActive;
auto *app = v->app;
app->enqueue(
[w, app, id, generation, image]
[w, app, id, generation, image, active]
{
if (!active->load()) return;
auto result = new PreviewResult{generation, id, {}, {}};
try
{
auto clip = app->history->load(id);
if (!active->load()) { delete result; return; }
if (image)
result->pixels = decodeImage(clip, 208, 128);
else
result->rtf = richPreview(clip);
else {
result->text.rtf = richPreview(clip, false);
if (result->text.rtf.empty()) {
result->text.plain = clip.text();
if (result->text.plain.size() > 4096) result->text.plain.resize(4096);
}
}
}
catch (const std::exception &)
{
}
if (!PostMessageW(w, PreviewMessage, 0, (LPARAM)result))
if (!active->load() || !PostMessageW(w, PreviewMessage, 0, (LPARAM)result))
delete result;
});
}
@@ -602,9 +618,13 @@ void drawHistoryRow(HWND w, HistoryView *v, const DRAWITEMSTRUCT &draw)
if (!entry.image && !entry.files)
{
auto rich = v->richTexts.find(entry.id);
if (rich != v->richTexts.end() && v->rich && !rich->second.empty())
if (rich != v->richTexts.end() && rich->second.rtf.empty())
text = (entry.pinned ? L"★ " : L"") + rich->second.plain;
if (rich != v->richTexts.end() && !rich->second.rtf.empty() && !v->rich)
v->rich = createRichText(w, 105, false);
if (rich != v->richTexts.end() && v->rich && !rich->second.rtf.empty())
{
setRichText(v->rich, rich->second);
setRichText(v->rich, rich->second.rtf);
CHARFORMAT2W format{};
format.cbSize = sizeof(format);
format.dwMask = CFM_COLOR;
@@ -825,14 +845,14 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
visible.insert(v->items[i].id);
if(v->items[i].id==result->id) { changed=intersection; repaint=true; }
}
if (!result->rtf.empty())
v->richTexts[result->id] = std::move(result->rtf);
if (!result->text.rtf.empty() || !result->text.plain.empty())
v->richTexts[result->id] = std::move(result->text);
else
v->thumbnails[result->id] = std::move(result->pixels);
trimHistoryPreviews(v->thumbnails,visible,32,HistoryThumbnailBudget,
[](const Pixels &p) { return p.bgra.capacity(); });
trimHistoryPreviews(v->richTexts,visible,32,HistoryRichTextBudget,
[](const std::string &s) { return s.capacity(); });
[](const TextPreview &s) { return s.rtf.capacity() + s.plain.capacity() * sizeof(wchar_t); });
if(repaint) InvalidateRect(list,&changed,FALSE);
return 0;
}
@@ -889,18 +909,19 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
v->rememberedTop = top >= 0 && top < (int)v->items.size() ? v->items[top].id : L"";
KillTimer(w, 1);
v->closed = true;
v->previewActive->store(false);
++v->generation; // Discard search/preview results queued before closing.
ShowWindow(w, SW_HIDE);
SendDlgItemMessageW(w,102,LB_RESETCONTENT,0,0);
std::vector<Entry>().swap(v->items);
v->thumbnails.clear();
v->richTexts.clear();
if (v->rich)
SetWindowTextW(v->rich, L"");
if (v->rich) SetWindowTextW(v->rich, L"");
v->pending.clear();
return 0;
}
case WM_NCDESTROY:
v->previewActive->store(false);
disposeFont(w);
v->app->historyWindow = nullptr;
delete v;
@@ -1702,7 +1723,6 @@ void showHistory(App &app, bool visible)
HWND w = makeWindow(app, L"KSwitcher.History", L"Clipboard history", historyProc, 760, 500);
app.historyWindow = w;
windowLayoutState(w)->history = true;
((HistoryView *)GetWindowLongPtrW(w, GWLP_USERDATA))->rich = createRichText(w, 105, false);
label(w, L"&Search clipboard history", 16, 12, 500);
edit(w, 101, 16, 38, 728);
auto list = control(w, L"LISTBOX", L"",
+1 -1
View File
@@ -2,7 +2,7 @@
#include "platform.h"
namespace ks
{
inline constexpr wchar_t AppVersion[] = L"0.6.37";
inline constexpr wchar_t AppVersion[] = L"0.6.38";
inline constexpr UINT UpdateMessage = WM_APP + 7;
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;