Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b08c5ff8ff |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required(VERSION 3.24)
|
||||||
project(KSwitcher VERSION 0.6.32 LANGUAGES CXX RC)
|
project(KSwitcher VERSION 0.6.33 LANGUAGES CXX RC)
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -16,10 +16,15 @@ Run `build/KSwitcher.exe`, or extract `dist/KSwitcher-win-x64.zip` and run the e
|
|||||||
The build also refreshes the release copy in `build/release/`. Exit KSwitcher before rebuilding.
|
The build also refreshes the release copy in `build/release/`. Exit KSwitcher before rebuilding.
|
||||||
The application lives in the notification area. Right-click its icon for Settings, Snippets and history.
|
The application lives in the notification area. Right-click its icon for Settings, Snippets and history.
|
||||||
Double-click the tray icon to open Settings, the bold default menu item.
|
Double-click the tray icon to open Settings, the bold default menu item.
|
||||||
|
Closing Settings with unsaved changes (including accepted auto-correction or OCR options)
|
||||||
|
asks whether to save, discard changes, or keep editing. Unchanged settings close without a prompt.
|
||||||
|
If saving fails, the window stays open with your edits intact.
|
||||||
Menus support access keys: press the underlined letter while the menu is open (for example,
|
Menus support access keys: press the underlined letter while the menu is open (for example,
|
||||||
S for Settings or X for Exit in the tray menu). In forms, press Alt+the underlined letter
|
S for Settings or X for Exit in the tray menu). In forms, press Alt+the underlined letter
|
||||||
to activate a button, toggle a checkbox or focus a labeled field. Press Alt to reveal
|
to activate a button, toggle a checkbox or focus a labeled field. Dialog underline visibility
|
||||||
the underlines when Windows hides them. Shortcut-capture fields still record Alt combinations;
|
still follows Windows preferences; press Alt in a dialog to reveal them.
|
||||||
|
From v0.6.33, tray and history menus show access letters in the right-hand column.
|
||||||
|
Press the menu letter without Alt: Alt dismisses an open menu. Shortcut-capture fields still record Alt combinations;
|
||||||
press Tab to leave a shortcut field before using form access keys.
|
press Tab to leave a shortcut field before using form access keys.
|
||||||
Settings, the snippet editor and log viewer run on demand in **KSwitcher.Config.exe**, beside the main executable.
|
Settings, the snippet editor and log viewer run on demand in **KSwitcher.Config.exe**, beside the main executable.
|
||||||
Repeated launches activate the existing configurator; closing its last window exits that process.
|
Repeated launches activate the existing configurator; closing its last window exits that process.
|
||||||
|
|||||||
Binary file not shown.
+7
-5
@@ -1,14 +1,16 @@
|
|||||||
# Latest Windows release
|
# Latest Windows release
|
||||||
|
|
||||||
## v0.6.32 — 2026-11-03
|
## v0.6.33 — 2026-11-04
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
- Fix the screenshot panning regression test for high-DPI displays. It now derives the image zoom from the actual viewport dimensions, centers the image, and checks that both axes have enough clearance for the tested drag.
|
- Settings now detects unsaved changes when closing with the window close button, Alt+F4 or Cancel, including accepted auto-correction dictionary and OCR option edits.
|
||||||
- At 240 DPI (250% scaling), the previous fixture's 1600 × 1200 image still fit inside the 2468 × 1389 viewport. The editor correctly prevented panning, but the test incorrectly expected a 15 × 10 pixel movement.
|
- Choose Yes to save, apply and close; No to discard; or Cancel to keep editing (the default). Unchanged or reverted settings close without a prompt. Invalid edits can be discarded, and a failed save keeps the window and draft open.
|
||||||
- Application behavior, shortcuts, defaults and settings are unchanged. The verified Viber trailing-space recovery from v0.6.31 remains included. No screenshot-editor workaround is needed for this test failure.
|
- Tray and clipboard-history menus now show access letters in the right-hand column, including submenu items. Press the letter without Alt; Alt dismisses an open menu. Existing shortcut hints are preserved.
|
||||||
|
- Dialog access-key behavior is unchanged: use Alt+letter, with underline visibility following Windows preferences. Windows-wide preferences and global shortcut assignments are unchanged.
|
||||||
|
- OK in Auto-correction/OCR options still updates the Settings draft; Save or Yes in the close prompt applies it. Snippets and Sync retain their separate save flows.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
The full `build.ps1 -Stage -Test` run passed: 105 sync checks, shared wheel batching, 483 native regressions, 541 input/UI checks at 240 DPI, the full screenshot editor UI suite (including the corrected pan test), PNG saving, screenshot registration, 48 updater regressions and updater progress UI checks. All three packaged executable versions and all 12 manifest file sizes/SHA-256 hashes were verified; the package contains no personal data.
|
The full `build.ps1 -Stage -Test` run passed: 105 sync checks, shared wheel batching, 483 native regressions, 551 input/UI checks at 96 DPI, screenshot editor UI checks, PNG saving, isolated screenshot registration, 48 updater regressions and updater progress UI checks. All three executable versions and all 12 package manifest file sizes/SHA-256 hashes were verified. New regressions cover unchanged/reverted settings, cancelling closure, discarding invalid edits, saving and applying edits, personal dictionary changes, and visible menu hints including submenus and existing shortcut labels.
|
||||||
|
|
||||||
## Upgrade
|
## Upgrade
|
||||||
Choose **Update** in the tray menu. Legacy v0.3.0–v0.4.1 clients first receive the v0.4.2 compatibility bridge, then the current full release. v0.2.3 and earlier require manual installation.
|
Choose **Update** in the tray menu. Legacy v0.3.0–v0.4.1 clients first receive the v0.4.2 compatibility bridge, then the current full release. v0.2.3 and earlier require manual installation.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
37c8ba8a27279141ac74318c24adf8b4ff4f573d68d4abff571a2abef7e739a1 KSwitcher-win-x64.zip
|
e46d1f6d52aeb58cf96c74db64bd450bc5b828e0a5111673c19a6657fca1d214 KSwitcher-win-x64.zip
|
||||||
|
|||||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
|||||||
1 RT_MANIFEST "app.manifest"
|
1 RT_MANIFEST "app.manifest"
|
||||||
201 RCDATA "update.ps1"
|
201 RCDATA "update.ps1"
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 0,6,32,0
|
FILEVERSION 0,6,33,0
|
||||||
PRODUCTVERSION 0,6,32,0
|
PRODUCTVERSION 0,6,33,0
|
||||||
FILEOS VOS_NT_WINDOWS32
|
FILEOS VOS_NT_WINDOWS32
|
||||||
FILETYPE VFT_APP
|
FILETYPE VFT_APP
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -20,7 +20,7 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||||
VALUE "ProductName", "KSwitcher"
|
VALUE "ProductName", "KSwitcher"
|
||||||
VALUE "FileVersion", "0.6.32"
|
VALUE "FileVersion", "0.6.33"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
@@ -1654,6 +1654,7 @@ void App::trayMenu()
|
|||||||
GetCursorPos(&pt);
|
GetCursorPos(&pt);
|
||||||
pasteTarget = GetForegroundWindow();
|
pasteTarget = GetForegroundWindow();
|
||||||
SetForegroundWindow(window);
|
SetForegroundWindow(window);
|
||||||
|
showMenuAccessKeys(menu);
|
||||||
int cmd = TrackPopupMenu(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON, pt.x, pt.y, 0, window, nullptr);
|
int cmd = TrackPopupMenu(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON, pt.x, pt.y, 0, window, nullptr);
|
||||||
DestroyMenu(menu);
|
DestroyMenu(menu);
|
||||||
PostMessageW(window, WM_NULL, 0, 0);
|
PostMessageW(window, WM_NULL, 0, 0);
|
||||||
|
|||||||
@@ -159,6 +159,31 @@ void showRequestedWindow(HWND window)
|
|||||||
if (!IsWindowVisible(window))
|
if (!IsWindowVisible(window))
|
||||||
ShowWindow(window, SW_SHOW);
|
ShowWindow(window, SW_SHOW);
|
||||||
}
|
}
|
||||||
|
void showMenuAccessKeys(HMENU menu)
|
||||||
|
{
|
||||||
|
// Mouse-opened popups ignore the owner's keyboard-cue state.
|
||||||
|
// Preserve the mnemonic and expose it in the right-hand hint column.
|
||||||
|
for (int i = 0; i < GetMenuItemCount(menu); ++i)
|
||||||
|
{
|
||||||
|
if (auto child = GetSubMenu(menu, i)) showMenuAccessKeys(child);
|
||||||
|
int length = GetMenuStringW(menu, i, nullptr, 0, MF_BYPOSITION);
|
||||||
|
std::wstring text(length + 1, L'\0');
|
||||||
|
GetMenuStringW(menu, i, text.data(), (int)text.size(), MF_BYPOSITION);
|
||||||
|
text.resize(length);
|
||||||
|
if (text.find(L'\t') != text.npos) continue;
|
||||||
|
for (size_t p = 0; p + 1 < text.size(); ++p)
|
||||||
|
{
|
||||||
|
if (text[p] != L'&') continue;
|
||||||
|
if (text[p + 1] == L'&') { ++p; continue; }
|
||||||
|
text += L"\t" + upper(text.substr(p + 1, 1));
|
||||||
|
MENUITEMINFOW item{sizeof(item)};
|
||||||
|
item.fMask = MIIM_STRING;
|
||||||
|
item.dwTypeData = text.data();
|
||||||
|
SetMenuItemInfoW(menu, i, TRUE, &item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
bool activateWindow(HWND window)
|
bool activateWindow(HWND window)
|
||||||
{
|
{
|
||||||
if (!IsWindow(window))
|
if (!IsWindow(window))
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ HKL windowLayout(HWND);
|
|||||||
bool switchWindowLayout(HWND, HKL, const std::function<bool()> &valid);
|
bool switchWindowLayout(HWND, HKL, const std::function<bool()> &valid);
|
||||||
bool activateWindow(HWND);
|
bool activateWindow(HWND);
|
||||||
void showRequestedWindow(HWND);
|
void showRequestedWindow(HWND);
|
||||||
|
void showMenuAccessKeys(HMENU);
|
||||||
bool ownsClipboard(HWND);
|
bool ownsClipboard(HWND);
|
||||||
bool webViewDescendant(DWORD owner, DWORD host,
|
bool webViewDescendant(DWORD owner, DWORD host,
|
||||||
const std::function<std::optional<std::pair<DWORD, std::wstring>>(DWORD)> &read);
|
const std::function<std::optional<std::pair<DWORD, std::wstring>>(DWORD)> &read);
|
||||||
|
|||||||
+43
-2
@@ -867,6 +867,7 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
AppendMenuW(menu, MF_STRING, 113, L"&Delete");
|
AppendMenuW(menu, MF_STRING, 113, L"&Delete");
|
||||||
AppendMenuW(menu, MF_STRING, 114, selectedImage(w,v)?L"&Edit image in screenshot editor":L"&Open full item");
|
AppendMenuW(menu, MF_STRING, 114, selectedImage(w,v)?L"&Edit image in screenshot editor":L"&Open full item");
|
||||||
AppendMenuW(menu, MF_STRING, 115, L"&Copy text from image (OCR)");
|
AppendMenuW(menu, MF_STRING, 115, L"&Copy text from image (OCR)");
|
||||||
|
showMenuAccessKeys(menu);
|
||||||
int cmd = TrackPopupMenu(menu, TPM_RETURNCMD, p.x, p.y, 0, w, nullptr);
|
int cmd = TrackPopupMenu(menu, TPM_RETURNCMD, p.x, p.y, 0, w, nullptr);
|
||||||
DestroyMenu(menu);
|
DestroyMenu(menu);
|
||||||
if (cmd)
|
if (cmd)
|
||||||
@@ -1338,6 +1339,46 @@ void showOcrOptions(HWND parent, SettingsView *v)
|
|||||||
showRequestedWindow(w); SetForegroundWindow(w);
|
showRequestedWindow(w); SetForegroundWindow(w);
|
||||||
}
|
}
|
||||||
#include "ui_sync.inl"
|
#include "ui_sync.inl"
|
||||||
|
bool settingsChanged(HWND w, SettingsView *v)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return encodeSettings(collectSettings(w, v)) != encodeSettings(v->app->settings);
|
||||||
|
}
|
||||||
|
catch (const std::exception &)
|
||||||
|
{
|
||||||
|
// Invalid edits are unsaved changes too. Allow discarding them without
|
||||||
|
// requiring the user to repair the fields first.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void closeSettings(HWND w, SettingsView *v)
|
||||||
|
{
|
||||||
|
if (settingsChanged(w, v))
|
||||||
|
{
|
||||||
|
int choice = MessageBoxW(w,
|
||||||
|
L"You have unsaved settings changes. Save them before closing?\n\n"
|
||||||
|
L"Yes: save and close.\nNo: discard changes.\nCancel: keep editing.",
|
||||||
|
L"KSwitcher — Unsaved settings", MB_YESNOCANCEL | MB_ICONWARNING | MB_DEFBUTTON3);
|
||||||
|
if (choice != IDYES && choice != IDNO)
|
||||||
|
return;
|
||||||
|
if (choice == IDYES)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (v->app->busy)
|
||||||
|
throw std::runtime_error("Wait for the current text operation to finish");
|
||||||
|
v->app->applySettings(collectSettings(w, v));
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
error(w, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DestroyWindow(w);
|
||||||
|
}
|
||||||
LRESULT CALLBACK settingsProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
LRESULT CALLBACK settingsProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
||||||
{
|
{
|
||||||
if (msg == WM_DPICHANGED)
|
if (msg == WM_DPICHANGED)
|
||||||
@@ -1403,7 +1444,7 @@ LRESULT CALLBACK settingsProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
}
|
}
|
||||||
if (id == IDCANCEL)
|
if (id == IDCANCEL)
|
||||||
{
|
{
|
||||||
DestroyWindow(w);
|
closeSettings(w, v);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (id == 250)
|
if (id == 250)
|
||||||
@@ -1454,7 +1495,7 @@ LRESULT CALLBACK settingsProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
DestroyWindow(w);
|
closeSettings(w, v);
|
||||||
return 0;
|
return 0;
|
||||||
case WM_NCDESTROY:
|
case WM_NCDESTROY:
|
||||||
disposeFont(w);
|
disposeFont(w);
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
namespace ks
|
namespace ks
|
||||||
{
|
{
|
||||||
inline constexpr wchar_t AppVersion[] = L"0.6.32";
|
inline constexpr wchar_t AppVersion[] = L"0.6.33";
|
||||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||||
|
|||||||
@@ -5,6 +5,19 @@
|
|||||||
#include <commctrl.h>
|
#include <commctrl.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
static int settingsCloseChoice = IDCANCEL;
|
||||||
|
static int settingsClosePrompts = 0;
|
||||||
|
static void CALLBACK answerSettingsClose(HWND, UINT, UINT_PTR, DWORD)
|
||||||
|
{
|
||||||
|
HWND dialog = FindWindowW(L"#32770", L"KSwitcher — Unsaved settings");
|
||||||
|
DWORD process{};
|
||||||
|
if (dialog && GetWindowThreadProcessId(dialog, &process) == GetCurrentThreadId() &&
|
||||||
|
process == GetCurrentProcessId())
|
||||||
|
{
|
||||||
|
++settingsClosePrompts;
|
||||||
|
SendMessageW(dialog, WM_COMMAND, settingsCloseChoice, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
LRESULT CALLBACK countControlMoves(HWND w, UINT message, WPARAM wp, LPARAM lp, UINT_PTR, DWORD_PTR data)
|
LRESULT CALLBACK countControlMoves(HWND w, UINT message, WPARAM wp, LPARAM lp, UINT_PTR, DWORD_PTR data)
|
||||||
{
|
{
|
||||||
if (message == WM_WINDOWPOSCHANGING) ++*reinterpret_cast<int *>(data);
|
if (message == WM_WINDOWPOSCHANGING) ++*reinterpret_cast<int *>(data);
|
||||||
@@ -80,6 +93,25 @@ int main(int argc, char **argv) {
|
|||||||
try {
|
try {
|
||||||
INITCOMMONCONTROLSEX controls{sizeof(controls),ICC_STANDARD_CLASSES|ICC_LISTVIEW_CLASSES};
|
INITCOMMONCONTROLSEX controls{sizeof(controls),ICC_STANDARD_CLASSES|ICC_LISTVIEW_CLASSES};
|
||||||
InitCommonControlsEx(&controls);
|
InitCommonControlsEx(&controls);
|
||||||
|
{
|
||||||
|
HMENU menu = CreatePopupMenu(), child = CreatePopupMenu();
|
||||||
|
AppendMenuW(menu,MF_STRING | MF_CHECKED,1,L"&Settings…");
|
||||||
|
AppendMenuW(menu,MF_STRING,2,L"Copy\tCtrl+C");
|
||||||
|
AppendMenuW(child,MF_STRING,3,L"Rock && &Roll");
|
||||||
|
AppendMenuW(menu,MF_POPUP,(UINT_PTR)child,L"&Pause");
|
||||||
|
ks::showMenuAccessKeys(menu);
|
||||||
|
ks::showMenuAccessKeys(menu);
|
||||||
|
wchar_t label[128]{};
|
||||||
|
GetMenuStringW(menu,1,label,128,MF_BYCOMMAND);
|
||||||
|
require(std::wstring(label)==L"&Settings…\tS" &&
|
||||||
|
(GetMenuState(menu,1,MF_BYCOMMAND)&MF_CHECKED),
|
||||||
|
"visible menu mnemonic is idempotent and preserves item state");
|
||||||
|
GetMenuStringW(menu,2,label,128,MF_BYCOMMAND);
|
||||||
|
require(std::wstring(label)==L"Copy\tCtrl+C","existing menu shortcut hints are retained");
|
||||||
|
GetMenuStringW(child,3,label,128,MF_BYCOMMAND);
|
||||||
|
require(std::wstring(label)==L"Rock && &Roll\tR","submenu hints skip escaped ampersands");
|
||||||
|
DestroyMenu(menu);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
ks::App app(GetModuleHandleW(nullptr),true,temp);
|
ks::App app(GetModuleHandleW(nullptr),true,temp);
|
||||||
ks::InputRegressionAccess::setup(app);
|
ks::InputRegressionAccess::setup(app);
|
||||||
@@ -629,6 +661,49 @@ int main(int argc, char **argv) {
|
|||||||
fitted=ks::fitWindowRect({2000,2000,2600,2400},{-1920,0,0,1080});
|
fitted=ks::fitWindowRect({2000,2000,2600,2400},{-1920,0,0,1080});
|
||||||
require(fitted.left==-600&&fitted.top==680&&fitted.right==0,"negative-coordinate monitor supported");
|
require(fitted.left==-600&&fitted.top==680&&fitted.right==0,"negative-coordinate monitor supported");
|
||||||
ks::showSettings(app,false);
|
ks::showSettings(app,false);
|
||||||
|
{
|
||||||
|
auto timer = SetTimer(nullptr, 0, 10, answerSettingsClose);
|
||||||
|
require(timer != 0, "settings close prompt responder");
|
||||||
|
SendMessageW(app.settingsWindow, WM_CLOSE, 0, 0);
|
||||||
|
require(!app.settingsWindow && settingsClosePrompts == 0,
|
||||||
|
"unchanged settings close without prompting");
|
||||||
|
ks::showSettings(app,false);
|
||||||
|
auto settingsWindow = app.settingsWindow;
|
||||||
|
auto initial = SendDlgItemMessageW(settingsWindow,205,BM_GETCHECK,0,0);
|
||||||
|
SendDlgItemMessageW(settingsWindow,205,BM_SETCHECK,!initial,0);
|
||||||
|
SendMessageW(settingsWindow, WM_CLOSE, 0, 0);
|
||||||
|
require(IsWindow(settingsWindow) && settingsClosePrompts == 1,
|
||||||
|
"cancel close preserves changed settings");
|
||||||
|
SendDlgItemMessageW(settingsWindow,205,BM_SETCHECK,initial,0);
|
||||||
|
SendMessageW(settingsWindow, WM_CLOSE, 0, 0);
|
||||||
|
require(!app.settingsWindow && settingsClosePrompts == 1,
|
||||||
|
"reverted settings close without prompting");
|
||||||
|
ks::showSettings(app,false);
|
||||||
|
SetDlgItemTextW(app.settingsWindow,240,L"invalid");
|
||||||
|
settingsCloseChoice = IDNO;
|
||||||
|
SendMessageW(app.settingsWindow,WM_COMMAND,IDCANCEL,0);
|
||||||
|
require(!app.settingsWindow && settingsClosePrompts == 2,
|
||||||
|
"Cancel button allows discarding invalid edits");
|
||||||
|
auto originalSettings = app.settings;
|
||||||
|
// The isolated harness has no keyboard hook; save without shortcuts.
|
||||||
|
app.settings.toggle = app.settings.casing = app.settings.translit =
|
||||||
|
app.settings.undo = app.settings.clipboard = app.settings.screenshot = L"";
|
||||||
|
app.settings.direct.assign(app.layouts.size(), L"");
|
||||||
|
ks::showSettings(app,false);
|
||||||
|
SendDlgItemMessageW(app.settingsWindow,208,BM_SETCHECK,
|
||||||
|
originalSettings.writeLog ? BST_UNCHECKED : BST_CHECKED,0);
|
||||||
|
settingsCloseChoice = IDYES;
|
||||||
|
SendMessageW(app.settingsWindow,WM_CLOSE,0,0);
|
||||||
|
require(!app.settingsWindow && settingsClosePrompts == 3 &&
|
||||||
|
app.settings.writeLog != originalSettings.writeLog &&
|
||||||
|
ks::loadSettings(app.data / L"settings.json",app.layouts).writeLog == app.settings.writeLog,
|
||||||
|
"Yes saves settings to disk and applies them before closing");
|
||||||
|
app.settings = originalSettings;
|
||||||
|
ks::setLoggingEnabled(originalSettings.writeLog);
|
||||||
|
KillTimer(nullptr,timer);
|
||||||
|
settingsCloseChoice = IDCANCEL;
|
||||||
|
ks::showSettings(app,false);
|
||||||
|
}
|
||||||
require(GetDlgItem(app.settingsWindow,258)!=nullptr,"sync available without local opt-in");
|
require(GetDlgItem(app.settingsWindow,258)!=nullptr,"sync available without local opt-in");
|
||||||
// Mnemonics only target visible controls; show without taking foreground focus.
|
// Mnemonics only target visible controls; show without taking foreground focus.
|
||||||
ShowWindow(app.settingsWindow,SW_SHOWNOACTIVATE);
|
ShowWindow(app.settingsWindow,SW_SHOWNOACTIVATE);
|
||||||
@@ -774,6 +849,14 @@ int main(int argc, char **argv) {
|
|||||||
SendDlgItemMessageW(autoOptions,415,BM_SETCHECK,BST_CHECKED,0);
|
SendDlgItemMessageW(autoOptions,415,BM_SETCHECK,BST_CHECKED,0);
|
||||||
SendMessageW(autoOptions,WM_COMMAND,IDOK,0);
|
SendMessageW(autoOptions,WM_COMMAND,IDOK,0);
|
||||||
require(!IsWindow(autoOptions),"personal words accepted without file editing");
|
require(!IsWindow(autoOptions),"personal words accepted without file editing");
|
||||||
|
{
|
||||||
|
auto timer = SetTimer(nullptr,0,10,answerSettingsClose);
|
||||||
|
auto before = settingsClosePrompts;
|
||||||
|
SendMessageW(app.settingsWindow,WM_CLOSE,0,0);
|
||||||
|
KillTimer(nullptr,timer);
|
||||||
|
require(app.settingsWindow && settingsClosePrompts == before + 1,
|
||||||
|
"accepted personal dictionary edits trigger unsaved settings warning");
|
||||||
|
}
|
||||||
SendMessageW(app.settingsWindow,WM_COMMAND,255,0);
|
SendMessageW(app.settingsWindow,WM_COMMAND,255,0);
|
||||||
autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
||||||
ShowWindow(autoOptions,SW_HIDE);
|
ShowWindow(autoOptions,SW_HIDE);
|
||||||
|
|||||||
Reference in New Issue
Block a user