Release v0.6.41: filter and remember pinned clipboard history

This commit is contained in:
2026-11-04 19:58:15 +02:00
parent 524a836cc0
commit 684ca1b3cc
14 changed files with 60 additions and 28 deletions
+1
View File
@@ -2078,6 +2078,7 @@ int App::execute()
historySelection = wide(state.value("selection", std::string{}));
historyTop = wide(state.value("top", std::string{}));
historyQuery = wide(state.value("query", std::string{}));
historyPinnedOnly = state.value("pinnedOnly", false);
} catch (...) {}
showHistory(*this);
remoteHistory("ready", std::to_wstring(reinterpret_cast<uintptr_t>(historyWindow)));
+2 -1
View File
@@ -37,6 +37,7 @@ class App
bool isolateHistory{};
unsigned historyEntryWindows{};
std::wstring historySelection, historyTop, historyQuery;
bool historyPinnedOnly{};
HINSTANCE instance{};
std::filesystem::path data;
std::vector<Layout> layouts;
@@ -80,7 +81,7 @@ class App
void refreshHistoryProcess();
void remoteHistory(const char *action, const std::wstring &id = {}, bool plain = false);
LRESULT historyRequest(HWND, const COPYDATASTRUCT &);
void saveHistoryView(const std::wstring &, const std::wstring &, const std::wstring &);
void saveHistoryView(const std::wstring &, const std::wstring &, const std::wstring &, bool);
void clearLog(bool previous);
void setHotkeyCapture(HWND);
bool isHotkey(DWORD, UINT);
+2 -2
View File
@@ -90,11 +90,11 @@ LRESULT App::historyRequest(HWND sender, const COPYDATASTRUCT &message)
return TRUE;
} catch (...) { return FALSE; }
}
void App::saveHistoryView(const std::wstring &selection, const std::wstring &top, const std::wstring &query)
void App::saveHistoryView(const std::wstring &selection, const std::wstring &top, const std::wstring &query, bool pinnedOnly)
{
if (!historyService) return;
try {
auto text = HistoryJson{{"selection", utf8(selection)}, {"top", utf8(top)}, {"query", utf8(query)}}.dump();
auto text = HistoryJson{{"selection", utf8(selection)}, {"top", utf8(top)}, {"query", utf8(query)}, {"pinnedOnly", pinnedOnly}}.dump();
atomicWrite(data / L"history-view.bin", protect(Bytes(text.begin(), text.end())));
} catch (...) {} // Closing the UI must still release its process if state cannot be saved.
}
+2 -1
View File
@@ -924,9 +924,10 @@ std::optional<std::wstring> History::ocrText(const std::wstring &id, const std::
if (bytes.empty() || bytes.front() != 1) throw std::runtime_error("Invalid OCR text format");
return wide(std::string(bytes.begin() + 1, bytes.end()));
}
std::vector<Entry> History::search(const std::wstring &query) const
std::vector<Entry> History::search(const std::wstring &query, bool pinnedOnly) const
{
auto items = entries();
if (pinnedOnly) std::erase_if(items, [](const Entry &entry) { return !entry.pinned; });
if (query.empty())
return items;
auto q = lower(query);
+1 -1
View File
@@ -43,7 +43,7 @@ class History
void remove(const std::wstring &);
void pin(const std::wstring &);
void clear();
std::vector<Entry> search(const std::wstring &) const;
std::vector<Entry> search(const std::wstring &, bool pinnedOnly = false) const;
};
Bytes encodeClip(const Clip &);
Clip decodeClip(const Bytes &);
+19 -5
View File
@@ -514,15 +514,16 @@ void search(HWND w, HistoryView *v)
v->previewActive = std::make_shared<std::atomic<bool>>(true);
v->closed = false;
auto q = value(w, 101);
const bool pinnedOnly = SendDlgItemMessageW(w, 117, CB_GETCURSEL, 0, 0) == 1;
auto generation = ++v->generation;
v->pending.clear();
auto *app = v->app;
app->enqueueHistorySearch(
[w, app, q, generation]
[w, app, q, generation, pinnedOnly]
{
try
{
auto result = new SearchResult{generation, app->history->search(q)};
auto result = new SearchResult{generation, app->history->search(q, pinnedOnly)};
if (!PostMessageW(w, ResultsMessage, 0, (LPARAM)result))
delete result;
}
@@ -739,6 +740,12 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
case WM_COMMAND:
{
int id = LOWORD(wp);
if (id == 117 && HIWORD(wp) == CBN_SELCHANGE)
{
KillTimer(w, 1);
search(w, v);
return 0;
}
if (id == 101 && HIWORD(wp) == EN_CHANGE)
{
// Reject an older result even during the debounce interval.
@@ -910,7 +917,8 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
v->rememberedSelection = selected(w, v);
int top = (int)SendDlgItemMessageW(w,102,LB_GETTOPINDEX,0,0);
v->rememberedTop = top >= 0 && top < (int)v->items.size() ? v->items[top].id : L"";
v->app->saveHistoryView(v->rememberedSelection, v->rememberedTop, value(w, 101));
v->app->saveHistoryView(v->rememberedSelection, v->rememberedTop, value(w, 101),
SendDlgItemMessageW(w, 117, CB_GETCURSEL, 0, 0) == 1);
KillTimer(w, 1);
v->closed = true;
v->previewActive->store(false);
@@ -1737,8 +1745,14 @@ 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;
label(w, L"&Search clipboard history", 16, 12, 500);
edit(w, 101, 16, 38, 728);
label(w, L"&Filter", 16, 12, 170);
auto filter = control(w, L"COMBOBOX", L"", WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL,
117, 16, 38, 170, 100);
SendMessageW(filter, CB_ADDSTRING, 0, (LPARAM)L"All items");
SendMessageW(filter, CB_ADDSTRING, 0, (LPARAM)L"★ Pinned only");
SendMessageW(filter, CB_SETCURSEL, app.historyPinnedOnly ? 1 : 0, 0);
label(w, L"&Search clipboard history", 194, 12, 500);
edit(w, 101, 194, 38, 550);
auto list = control(w, L"LISTBOX", L"",
WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER |
LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
+1 -1
View File
@@ -2,7 +2,7 @@
#include "platform.h"
namespace ks
{
inline constexpr wchar_t AppVersion[] = L"0.6.40";
inline constexpr wchar_t AppVersion[] = L"0.6.41";
inline constexpr UINT UpdateMessage = WM_APP + 7;
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;