From 252dbb58539d921a3dba3bb2b1e8452cdf67d104 Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:49:54 +0800 Subject: [PATCH] Settings: Generate bug report should tell user there is bug report generating (#40060) ### ## Summary of the Pull Request ### Bug report tool status tracking: Currently, After clicking the generate package button, button is still active, as we do not have bug report progress, this will confuse user whether they actually clicks the button. Add an enable status to acknowledge the bug generating ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments 1. Progress bar should be present in generating report place when there is bug report going on. 2. Runner&Settings should know each other when they trigger the bug report. 3. Runner tray icon menu item should be disabled when there is one bug report going on. 4. After bug report generation, everything should be like before. ## Validation Steps Performed https://github.com/user-attachments/assets/dcbf8e6e-c5e1-4d23-9dab-f16c11ed56cf --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .github/actions/spell-check/expect.txt | 1 + src/runner/bug_report.cpp | 69 +++++++++++++++++-- src/runner/bug_report.h | 42 ++++++++++- src/runner/settings_window.cpp | 19 +++++ src/runner/tray_icon.cpp | 24 ++++++- .../SettingsXAML/Views/GeneralPage.xaml | 18 ++++- .../SettingsXAML/Views/GeneralPage.xaml.cs | 49 ++++++++++++- .../ViewModels/GeneralViewModel.cs | 18 +++++ 8 files changed, 229 insertions(+), 11 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 75599dda0e..b4354e2f91 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -156,6 +156,7 @@ builttoroam BVal BValue byapp +BYCOMMAND BYPOSITION CALCRECT CALG diff --git a/src/runner/bug_report.cpp b/src/runner/bug_report.cpp index 9abfe6fa18..697bf518f7 100644 --- a/src/runner/bug_report.cpp +++ b/src/runner/bug_report.cpp @@ -4,17 +4,52 @@ #include #include -std::atomic_bool isBugReportThreadRunning = false; +BugReportManager& BugReportManager::instance() +{ + static BugReportManager instance; + return instance; +} -void launch_bug_report() noexcept +void BugReportManager::register_callback(const BugReportCallback& callback) +{ + std::lock_guard lock(m_callbacksMutex); + m_callbacks.push_back(callback); +} + +void BugReportManager::clear_callbacks() +{ + std::lock_guard lock(m_callbacksMutex); + m_callbacks.clear(); +} + +void BugReportManager::notify_observers(bool isRunning) +{ + std::lock_guard lock(m_callbacksMutex); + for (const auto& callback : m_callbacks) + { + try + { + callback(isRunning); + } + catch (...) + { + // Ignore callback exceptions to prevent one bad callback from affecting others + } + } +} + +void BugReportManager::launch_bug_report() noexcept { std::wstring bug_report_path = get_module_folderpath(); bug_report_path += L"\\Tools\\PowerToys.BugReportTool.exe"; - bool expected_isBugReportThreadRunning = false; - if (isBugReportThreadRunning.compare_exchange_strong(expected_isBugReportThreadRunning, true)) + bool expected_isBugReportRunning = false; + if (m_isBugReportRunning.compare_exchange_strong(expected_isBugReportRunning, true)) { - std::thread([bug_report_path]() { + // Notify observers that bug report is starting + notify_observers(true); + + std::thread([this, bug_report_path]() { SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE }; sei.lpFile = bug_report_path.c_str(); @@ -27,7 +62,29 @@ void launch_bug_report() noexcept MessageBoxW(nullptr, bugreport_success.c_str(), L"PowerToys", MB_OK); } - isBugReportThreadRunning.store(false); + m_isBugReportRunning.store(false); + // Notify observers that bug report has finished + notify_observers(false); }).detach(); } + else + { + notify_observers(false); + } +} + +bool BugReportManager::is_bug_report_running() const noexcept +{ + return m_isBugReportRunning.load(); +} + +// Legacy functions for backward compatibility +void launch_bug_report() noexcept +{ + BugReportManager::instance().launch_bug_report(); +} + +bool is_bug_report_running() noexcept +{ + return BugReportManager::instance().is_bug_report_running(); } diff --git a/src/runner/bug_report.h b/src/runner/bug_report.h index 2d7084ea21..6edb1c6ba3 100644 --- a/src/runner/bug_report.h +++ b/src/runner/bug_report.h @@ -1,3 +1,43 @@ #pragma once -void launch_bug_report() noexcept; \ No newline at end of file +#include +#include +#include + +// Observer pattern for bug report status changes +using BugReportCallback = std::function; + +class BugReportManager +{ +public: + static BugReportManager& instance(); + + // Register a callback to be notified when bug report status changes + void register_callback(const BugReportCallback& callback); + + // Remove all callbacks (useful for cleanup) + void clear_callbacks(); + + // Launch bug report and notify observers + void launch_bug_report() noexcept; + + // Check if bug report is currently running + bool is_bug_report_running() const noexcept; + +private: + BugReportManager() = default; + ~BugReportManager() = default; + BugReportManager(const BugReportManager&) = delete; + BugReportManager& operator=(const BugReportManager&) = delete; + + // Notify all registered callbacks + void notify_observers(bool isRunning); + + std::atomic_bool m_isBugReportRunning = false; + std::vector m_callbacks; + mutable std::mutex m_callbacksMutex; +}; + +// Legacy functions for backward compatibility +void launch_bug_report() noexcept; +bool is_bug_report_running() noexcept; \ No newline at end of file diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 265d3e3dc0..966ea95163 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -227,6 +227,14 @@ void dispatch_received_json(const std::wstring& json_to_parse) { launch_bug_report(); } + else if (name == L"bug_report_status") + { + json::JsonObject result; + result.SetNamedValue(L"bug_report_running", winrt::Windows::Data::Json::JsonValue::CreateBooleanValue(is_bug_report_running())); + std::unique_lock lock{ ipc_mutex }; + if (current_settings_ipc) + current_settings_ipc->send(result.Stringify().c_str()); + } else if (name == L"killrunner") { const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr); @@ -488,7 +496,18 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op std::unique_lock lock{ ipc_mutex }; current_settings_ipc = new TwoWayPipeMessageIPC(powertoys_pipe_name, settings_pipe_name, receive_json_send_to_main_thread); current_settings_ipc->start(hToken); + + // Register callback for bug report status changes + BugReportManager::instance().register_callback([](bool isRunning) { + json::JsonObject result; + result.SetNamedValue(L"bug_report_running", winrt::Windows::Data::Json::JsonValue::CreateBooleanValue(isRunning)); + + std::unique_lock lock{ ipc_mutex }; + if (current_settings_ipc) + current_settings_ipc->send(result.Stringify().c_str()); + }); } + g_settings_process_id = process_info.dwProcessId; if (process_info.hProcess) diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index 30cca89951..7f819209e5 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -207,6 +207,8 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam change_menu_item_text(ID_EXIT_MENU_COMMAND, exit_menuitem_label.data()); change_menu_item_text(ID_SHOW_TRAY_ICON_MENU_COMMAND, show_tray_icon_menuitem_label.data()); change_menu_item_text(ID_REPORT_BUG_COMMAND, submit_bug_menuitem_label.data()); + bool bug_report_disabled = is_bug_report_running(); + EnableMenuItem(h_sub_menu, ID_REPORT_BUG_COMMAND, MF_BYCOMMAND | (bug_report_disabled ? MF_GRAYED : MF_ENABLED)); change_menu_item_text(ID_DOCUMENTATION_MENU_COMMAND, documentation_menuitem_label.data()); change_menu_item_text(ID_QUICK_ACCESS_MENU_COMMAND, quick_access_menuitem_label.data()); } @@ -269,6 +271,14 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam return DefWindowProc(window, message, wparam, lparam); } +void update_bug_report_menu_status(bool isRunning) +{ + if (h_sub_menu != nullptr) + { + EnableMenuItem(h_sub_menu, ID_REPORT_BUG_COMMAND, MF_BYCOMMAND | (isRunning ? MF_GRAYED : MF_ENABLED)); + } +} + void start_tray_icon(bool isProcessElevated) { auto h_instance = reinterpret_cast(&__ImageBase); @@ -319,6 +329,16 @@ void start_tray_icon(bool isProcessElevated) ChangeWindowMessageFilterEx(hwnd, WM_COMMAND, MSGFLT_ALLOW, nullptr); tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE; + + // Register callback to update bug report menu item status + BugReportManager::instance().register_callback([](bool isRunning) { + dispatch_run_on_main_ui_thread([](PVOID data) { + bool* running = static_cast(data); + update_bug_report_menu_status(*running); + delete running; + }, + new bool(isRunning)); + }); } } @@ -334,6 +354,8 @@ void stop_tray_icon() { if (tray_icon_created) { + // Clear bug report callbacks + BugReportManager::instance().clear_callbacks(); SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0); } -} +} \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml index 13d6ee5c8f..a80094e5a2 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml @@ -454,11 +454,25 @@