Release v0.6.33: warn about unsaved settings and expose menu access keys
This commit is contained in:
@@ -5,6 +5,19 @@
|
||||
#include <commctrl.h>
|
||||
#include <iostream>
|
||||
#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)
|
||||
{
|
||||
if (message == WM_WINDOWPOSCHANGING) ++*reinterpret_cast<int *>(data);
|
||||
@@ -80,6 +93,25 @@ int main(int argc, char **argv) {
|
||||
try {
|
||||
INITCOMMONCONTROLSEX controls{sizeof(controls),ICC_STANDARD_CLASSES|ICC_LISTVIEW_CLASSES};
|
||||
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::InputRegressionAccess::setup(app);
|
||||
@@ -629,6 +661,49 @@ int main(int argc, char **argv) {
|
||||
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");
|
||||
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");
|
||||
// Mnemonics only target visible controls; show without taking foreground focus.
|
||||
ShowWindow(app.settingsWindow,SW_SHOWNOACTIVATE);
|
||||
@@ -774,6 +849,14 @@ int main(int argc, char **argv) {
|
||||
SendDlgItemMessageW(autoOptions,415,BM_SETCHECK,BST_CHECKED,0);
|
||||
SendMessageW(autoOptions,WM_COMMAND,IDOK,0);
|
||||
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);
|
||||
autoOptions=FindWindowW(L"KSwitcher.AutoOptions",nullptr);
|
||||
ShowWindow(autoOptions,SW_HIDE);
|
||||
|
||||
Reference in New Issue
Block a user