From fef2648b39164eb136449b9763a0b3cc016ecd3c Mon Sep 17 00:00:00 2001 From: Takayama Fumihiko Date: Mon, 2 Dec 2024 23:01:39 +0900 Subject: [PATCH] Add history_index to open_application --- .../components_manager.hpp | 8 ++ .../software_function_handler.hpp | 83 +++++++++++++++++++ .../open_application.hpp | 16 ++++ .../json/errors/event_definition_errors.jsonc | 11 +++ .../src/types/src/software_function_test.hpp | 11 +++ 5 files changed, 129 insertions(+) diff --git a/src/core/console_user_server/include/console_user_server/components_manager.hpp b/src/core/console_user_server/include/console_user_server/components_manager.hpp index b057306c2..c2c2b957a 100644 --- a/src/core/console_user_server/include/console_user_server/components_manager.hpp +++ b/src/core/console_user_server/include/console_user_server/components_manager.hpp @@ -254,6 +254,14 @@ class components_manager final : public pqrs::dispatcher::extra::dispatcher_clie if (auto m = pqrs::osx::frontmost_application_monitor::monitor::get_shared_monitor().lock()) { m->frontmost_application_changed.connect([this](auto&& application_ptr) { if (application_ptr) { + if (software_function_handler_) { + software_function_handler_->add_frontmost_application_history(*application_ptr); + } + + // + // Notify the grabber of frontmost application changed. + // + if (application_ptr->get_bundle_identifier() == "org.pqrs.Karabiner.EventViewer" || application_ptr->get_bundle_identifier() == "org.pqrs.Karabiner-EventViewer") { return; diff --git a/src/core/console_user_server/include/console_user_server/software_function_handler.hpp b/src/core/console_user_server/include/console_user_server/software_function_handler.hpp index a22d0c083..7de843a0e 100644 --- a/src/core/console_user_server/include/console_user_server/software_function_handler.hpp +++ b/src/core/console_user_server/include/console_user_server/software_function_handler.hpp @@ -1,6 +1,7 @@ #pragma once #include "logger.hpp" +#include #include #include #include @@ -34,6 +35,54 @@ class software_function_handler final : public pqrs::dispatcher::extra::dispatch } } + void add_frontmost_application_history(const pqrs::osx::frontmost_application_monitor::application& application) { + // + // Remove any identical entries that already exist. + // + // If the same application appears multiple times in the history, + // it causes inconvenient behavior when switching between the same application repeatedly or after an application is closed. + // To address this, duplicates should be eliminated from the history. + // + // For example, consider the following application switching sequence: + // + // 1. Mail + // 2. Terminal + // 3. Safari + // 4. Terminal + // + // As a user, we would expect the app two steps back to be Mail, not Terminal. + // + // Similarly, if Terminal is closed in this state, the focus will return to Safari. + // If duplicates are not removed, the history will look like this: + // + // 1. Mail + // 2. Terminal + // 3. Safari + // 4. Terminal + // 5. Safari + // + // In this scenario, if we try to switch to the previous application, + // Terminal (which is already closed) will be excluded, and the next app in the history, "3. Safari", will be selected. + // But as a user, it would feel more natural for Mail to be selected instead. + // + // By removing duplicates from the history, such behavior can be achieved, + // resulting in a smoother and more intuitive user experience. + // + + std::erase(frontmost_application_history_, application); + + // + // Add to history + // + + frontmost_application_history_.push_front(application); + + size_t max_size = 20; + while (frontmost_application_history_.size() > max_size) { + frontmost_application_history_.pop_back(); + } + } + private: void execute_cg_event_double_click(const software_function_details::cg_event_double_click& cg_event_double_click) { if (!check_trusted_) { @@ -64,6 +113,38 @@ class software_function_handler final : public pqrs::dispatcher::extra::dispatch pqrs::osx::workspace::open_application_by_bundle_identifier(*v); } else if (auto v = open_application.get_file_path()) { pqrs::osx::workspace::open_application_by_file_path(*v); + } else if (auto v = open_application.get_history_index()) { + auto history_index = *v; + for (const auto& h : frontmost_application_history_) { + // Target only applications that are currently running. + + // Since there are cases where the bundle paths differ even if the bundle_identifier is the same, prioritize using the bundle path. + if (auto bundle_path = h.get_bundle_path()) { + if (!pqrs::osx::workspace::application_running_by_file_path(*bundle_path)) { + continue; + } + if (history_index == 0) { + pqrs::osx::workspace::open_application_by_file_path(*bundle_path); + } + + } else if (auto bundle_identifier = h.get_bundle_identifier()) { + if (!pqrs::osx::workspace::application_running_by_bundle_identifier(*bundle_identifier)) { + continue; + } + if (history_index == 0) { + pqrs::osx::workspace::open_application_by_bundle_identifier(*bundle_identifier); + } + + } else { + continue; + } + + if (history_index > 0) { + --history_index; + } else { + break; + } + } } } @@ -89,6 +170,8 @@ class software_function_handler final : public pqrs::dispatcher::extra::dispatch private: bool check_trusted_; + // Stored in order from the newest at the beginning. + std::deque frontmost_application_history_; }; } // namespace console_user_server } // namespace krbn diff --git a/src/share/types/software_function_details/open_application.hpp b/src/share/types/software_function_details/open_application.hpp index 853a87ab7..bb9502c6f 100644 --- a/src/share/types/software_function_details/open_application.hpp +++ b/src/share/types/software_function_details/open_application.hpp @@ -29,11 +29,20 @@ class open_application { file_path_ = value; } + const std::optional& get_history_index(void) const { + return history_index_; + } + + void set_history_index(const std::optional& value) { + history_index_ = value; + } + constexpr bool operator==(const open_application&) const = default; private: std::optional bundle_identifier_; std::optional file_path_; + std::optional history_index_; }; inline void to_json(nlohmann::json& json, const open_application& value) { @@ -43,6 +52,9 @@ inline void to_json(nlohmann::json& json, const open_application& value) { if (auto v = value.get_file_path()) { json["file_path"] = *v; } + if (auto v = value.get_history_index()) { + json["history_index"] = *v; + } } inline void from_json(const nlohmann::json& json, open_application& value) { @@ -55,6 +67,9 @@ inline void from_json(const nlohmann::json& json, open_application& value) { } else if (k == "file_path") { pqrs::json::requires_string(v, "`" + k + "`"); value.set_file_path(v.get()); + } else if (k == "history_index") { + pqrs::json::requires_number(v, "`" + k + "`"); + value.set_history_index(v.get()); } else { throw pqrs::json::unmarshal_error(fmt::format("unknown key: `{0}`", k)); } @@ -71,6 +86,7 @@ struct hash final { pqrs::hash::combine(h, value.get_bundle_identifier()); pqrs::hash::combine(h, value.get_file_path()); + pqrs::hash::combine(h, value.get_history_index()); return h; } diff --git a/tests/src/manipulator_types/json/errors/event_definition_errors.jsonc b/tests/src/manipulator_types/json/errors/event_definition_errors.jsonc index a5eea8a02..6a8167215 100644 --- a/tests/src/manipulator_types/json/errors/event_definition_errors.jsonc +++ b/tests/src/manipulator_types/json/errors/event_definition_errors.jsonc @@ -409,6 +409,17 @@ }, "error": "`software_function` error: `open_application` error: `file_path` must be string, but is `null`" }, + { + "class": "event_definition", + "input": { + "software_function": { + "open_application": { + "history_index": null + } + } + }, + "error": "`software_function` error: `open_application` error: `history_index` must be number, but is `null`" + }, { "class": "event_definition", "input": { diff --git a/tests/src/types/src/software_function_test.hpp b/tests/src/types/src/software_function_test.hpp index d0cdf282c..b3215abb2 100644 --- a/tests/src/types/src/software_function_test.hpp +++ b/tests/src/types/src/software_function_test.hpp @@ -62,6 +62,17 @@ void run_software_function_test(void) { expect(nlohmann::json(value) == json); } + { + auto json = nlohmann::json::object({ + {"history_index", 1}, + }); + + auto value = json.get(); + expect(1 == value.get_history_index()); + + expect(nlohmann::json(value) == json); + } + // // set_mouse_cursor_position //