feat(automation): add view.select dispatcher handler + tests

This commit is contained in:
SoftFever
2026-06-03 21:18:50 +08:00
parent 7ef89fdb9d
commit 9a16fb7c2e
5 changed files with 99 additions and 1 deletions

View File

@@ -101,6 +101,13 @@ public:
// Throws AutomationError(kErrLoadFailed) when nothing loads. Header stays wx-free:
// the concrete LoadStrategy is chosen inside WxUiBackend, not exposed here.
virtual int open_files(const std::vector<std::string>& paths) = 0;
// Select a top-level view/tab by stable name (e.g. "prepare", "preview", "home",
// "device", "project", "calibration", "multi_device") on the GUI thread. Returns
// the resulting tab index. Throws AutomationError(kErrNotFound) when the named
// view is unknown or not available in the current layout. The wx-specific
// name->tab mapping lives in WxUiBackend/MainFrame, not here.
virtual int select_view(const std::string& view) = 0;
};
}}} // namespace Slic3r::GUI::Automation

View File

@@ -154,6 +154,21 @@ std::vector<std::string> parse_paths(const nlohmann::json& params) {
throw AutomationError(kInvalidParams, "'paths' is empty");
return out;
}
// "view" must be a non-empty string naming a top-level tab. The name->tab mapping
// itself lives in the wx backend, so this only validates shape; throws kInvalidParams
// when view is missing, not a string, or empty.
std::string parse_view(const nlohmann::json& params) {
if (!params.is_object() || !params.contains("view"))
throw AutomationError(kInvalidParams, "view.select requires 'view'");
const auto& v = params.at("view");
if (!v.is_string())
throw AutomationError(kInvalidParams, "'view' must be a string");
std::string name = v.get<std::string>();
if (name.empty())
throw AutomationError(kInvalidParams, "'view' is empty");
return name;
}
} // namespace
namespace {
@@ -196,7 +211,7 @@ nlohmann::json JsonRpcDispatcher::m_version(const nlohmann::json&) {
{"capabilities", nlohmann::json::array({
"tree.dump","tree.find","widget.get","input.click","input.type",
"input.key","sync.wait_for","app.state","screenshot.window",
"file.open" })} };
"file.open","view.select" })} };
}
nlohmann::json JsonRpcDispatcher::dispatch(const nlohmann::json& request) {
@@ -222,6 +237,7 @@ nlohmann::json JsonRpcDispatcher::dispatch(const nlohmann::json& request) {
if (method == "app.state") return make_result(id, m_app_state(params));
if (method == "screenshot.window") return make_result(id, m_screenshot_window(params));
if (method == "file.open") return make_result(id, m_file_open(params));
if (method == "view.select") return make_result(id, m_view_select(params));
return make_error(id, kMethodNotFound, "unknown method: " + method);
} catch (const AutomationError& e) {
return make_error(id, e.code, e.what());
@@ -377,4 +393,10 @@ nlohmann::json JsonRpcDispatcher::m_file_open(const nlohmann::json& params) {
return { {"ok", true}, {"loaded", loaded} };
}
nlohmann::json JsonRpcDispatcher::m_view_select(const nlohmann::json& params) {
const std::string view = parse_view(params);
const int index = m_backend.select_view(view);
return { {"ok", true}, {"view", view}, {"index", index} };
}
}}} // namespace

View File

@@ -49,6 +49,7 @@ private:
nlohmann::json m_app_state(const nlohmann::json& params);
nlohmann::json m_screenshot_window(const nlohmann::json& params);
nlohmann::json m_file_open(const nlohmann::json& params);
nlohmann::json m_view_select(const nlohmann::json& params);
// Resolve a unique, actionable (enabled+visible) node from params["target"].
// Throws kErrNotFound (missing/ambiguous) or kErrNotActionable (disabled/hidden).