feat: release kswitcher 0.6.64
Add mouse button actions and application profiles, with volume controls and per-monitor settings layout fixes.
This commit is contained in:
1 parent
5a2106899d
commit
14f33012a3
23 files changed
+2233
-107
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "settings_merge.h"
|
||||
#include "mouse_buttons.h"
|
||||
#include <iostream>
|
||||
using namespace ks;
|
||||
int main(){try{
|
||||
@@ -17,5 +18,87 @@ int main(){try{
|
||||
draft=base;current=base;draft.chatCycles[L"telegram\nfirst"]={L"win:04090409"};current.chatCycles[L"telegram\nsecond"]={L"win:04220422"};
|
||||
merged=decodeSettings(mergeConfigurationSettings(encodeSettings(base),encodeSettings(draft),encodeSettings(current)),{});
|
||||
if(merged.chatCycles.size()!=2)throw std::runtime_error("independent nested changes lost");
|
||||
std::cout<<"Passed 5 settings-save merge checks\n";
|
||||
draft = base;
|
||||
current = base;
|
||||
draft.mouseBindings[L"chrome"][L"XButton1"] = {MouseActionType::Shortcut, {MOD_CONTROL, VK_TAB}};
|
||||
current.mouseBindings[L"chrome"][L"XButton2"] = {MouseActionType::Shortcut, {MOD_CONTROL | MOD_SHIFT, VK_TAB}};
|
||||
merged = decodeSettings(mergeConfigurationSettings(encodeSettings(base), encodeSettings(draft),
|
||||
encodeSettings(current)), {});
|
||||
if (merged.mouseBindings.at(L"chrome").size() != 2)
|
||||
throw std::runtime_error("independent mouse bindings lost");
|
||||
auto shared = sharedSyncSettings(merged);
|
||||
if (shared != sharedSyncSettings(base) ||
|
||||
restoreSyncSettings(shared, merged, {}, false).mouseBindings != merged.mouseBindings)
|
||||
throw std::runtime_error("mouse profiles must stay local during sync");
|
||||
auto withButtons = merged;
|
||||
draft = current = withButtons;
|
||||
draft.mouseBindings.at(L"chrome").erase(L"XButton1");
|
||||
current.mouseBindings.at(L"chrome")[L"XButton2"] = {MouseActionType::Shortcut, {MOD_CONTROL, VK_F4}};
|
||||
merged = decodeSettings(mergeConfigurationSettings(encodeSettings(withButtons), encodeSettings(draft),
|
||||
encodeSettings(current)), {});
|
||||
const auto &buttons = merged.mouseBindings.at(L"chrome");
|
||||
if (buttons.size() != 1 || buttons.at(L"XButton2") != MouseAction{MouseActionType::Shortcut, {MOD_CONTROL, VK_F4}})
|
||||
throw std::runtime_error("mouse button removal lost during merge");
|
||||
merged.mouseBindings[GlobalMouseProfile][L"Double middle"] = {MouseActionType::Mute};
|
||||
merged.mouseBindings[L"chrome"][L"Double middle"] = {MouseActionType::Standard};
|
||||
merged.mouseBindings[L"edge"][L"XButton1"] = {MouseActionType::Launch, {}, L"C:\\Tools\\app.exe"};
|
||||
merged.mouseBindings[L"edge"][L"XButton2"] = {MouseActionType::Launch, {}, L"calc.exe"};
|
||||
merged.mouseBindings[GlobalMouseProfile][L"Left"] = {MouseActionType::Shortcut, {MOD_CONTROL, 'W'}, {}, {L"chrome", L"msedge"}};
|
||||
merged.mouseBindings[GlobalMouseProfile][L"Middle"] = {MouseActionType::Back, {}, {}, {L"chrome"}};
|
||||
auto restored = decodeSettings(encodeSettings(merged), {});
|
||||
if (restored.mouseBindings != merged.mouseBindings ||
|
||||
effectiveMouseAction(restored, L"chrome", 7) ||
|
||||
!effectiveMouseAction(restored, L"edge", 7) ||
|
||||
effectiveMouseAction(restored, L"edge", 7)->type != MouseActionType::Mute)
|
||||
throw std::runtime_error("mouse action override or inheritance failed");
|
||||
if (!effectiveMouseAction(restored, L"chrome", 0) || !effectiveMouseAction(restored, L"msedge", 0) ||
|
||||
effectiveMouseAction(restored, L"explorer", 0) || !effectiveMouseAction(restored, L"chrome", 2) ||
|
||||
effectiveMouseAction(restored, L"msedge", 2) || !effectiveMouseAction(restored, L"explorer", 7))
|
||||
throw std::runtime_error("application restrictions or unrestricted fallback failed");
|
||||
draft = current = restored;
|
||||
draft.mouseBindings[GlobalMouseProfile][L"Middle"].applications.clear();
|
||||
current.mouseBindings[GlobalMouseProfile][L"Middle"].type = MouseActionType::Mute;
|
||||
const auto competing = mergeSyncProfile(encodeSettings(restored), encodeSettings(draft), encodeSettings(current));
|
||||
if (competing.conflicts.size() != 1)
|
||||
throw std::runtime_error("competing edits to a mouse action and its scope must conflict");
|
||||
Settings profiles;
|
||||
profiles.mouseBindings[L"name:Browsing"][L"XButton1"] = {MouseActionType::Forward};
|
||||
profiles.mouseBindings[L"name:Browsing"][L"XButton2"] = {MouseActionType::Back};
|
||||
profiles.mouseProfileApplications[L"name:Browsing"] = {L"chrome", L"msedge"};
|
||||
profiles.mouseBindings[L"name:Empty"];
|
||||
profiles.mouseBindings[GlobalMouseProfile][L"XButton1"] = {MouseActionType::VolumeUp};
|
||||
profiles.mouseBindings[GlobalMouseProfile][L"XButton2"] = {MouseActionType::VolumeDown};
|
||||
profiles.mouseBindings[GlobalMouseProfile][L"XButton2"].volumeStep = 7;
|
||||
auto resolve = [&](const wchar_t *app, int event, bool foreground = true)
|
||||
{
|
||||
const auto *action = effectiveMouseAction(profiles, app, event, foreground);
|
||||
return action ? action->type : MouseActionType::Standard;
|
||||
};
|
||||
profiles = decodeSettings(mergeConfigurationSettings(encodeSettings(base), encodeSettings(profiles), encodeSettings(base)), {});
|
||||
if (profiles.mouseBindings.at(GlobalMouseProfile).at(L"XButton1").volumeStep != DefaultMouseVolumeStep ||
|
||||
profiles.mouseBindings.at(GlobalMouseProfile).at(L"XButton2").volumeStep != 7)
|
||||
throw std::runtime_error("default and custom volume steps must round-trip independently");
|
||||
auto invalidVolume = profiles;
|
||||
invalidVolume.mouseBindings[GlobalMouseProfile][L"XButton1"].volumeStep = 0;
|
||||
rejected = false;
|
||||
try { validateSettings(invalidVolume); }
|
||||
catch (const std::runtime_error &) { rejected = true; }
|
||||
if (!rejected) throw std::runtime_error("invalid volume steps must be rejected");
|
||||
if (!profiles.mouseBindings.contains(L"name:Empty") ||
|
||||
resolve(L"chrome", 3) != MouseActionType::Forward || resolve(L"msedge", 4) != MouseActionType::Back ||
|
||||
resolve(L"chrome", 3, false) != MouseActionType::VolumeUp ||
|
||||
resolve(L"explorer", 4) != MouseActionType::VolumeDown)
|
||||
throw std::runtime_error("named profiles must persist and override fallback only for the matching foreground app");
|
||||
profiles.mouseBindings[L"name:Alternative"][L"XButton1"] = {MouseActionType::Back};
|
||||
profiles.mouseProfileApplications[L"name:Alternative"] = {L"chrome"};
|
||||
profiles.disabledMouseProfiles.insert(L"name:Browsing");
|
||||
profiles = decodeSettings(encodeSettings(profiles), {});
|
||||
if (resolve(L"chrome", 3) != MouseActionType::Back)
|
||||
throw std::runtime_error("disabled profiles must not resolve actions");
|
||||
profiles.disabledMouseProfiles.clear();
|
||||
rejected = false;
|
||||
try { validateSettings(profiles); }
|
||||
catch (const std::runtime_error &e) { rejected = std::string(e.what()).starts_with("Mouse action conflict:"); }
|
||||
if (!rejected) throw std::runtime_error("overlapping active profiles must be rejected");
|
||||
std::cout<<"Settings-save merge checks passed\n";
|
||||
}catch(const std::exception &e){std::cerr<<e.what()<<'\n';return 1;}}
|
||||
Reference in new issue
Block a user