Settings: Generate bug report should tell user there is bug report generating (#40060)

### <!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## 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

<!-- Please review the items on the PR checklist before submitting-->
## 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>
This commit is contained in:
Kai Tao 2025-06-17 14:49:54 +08:00 committed by GitHub
parent 655398e173
commit 252dbb5853
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 229 additions and 11 deletions

View File

@ -156,6 +156,7 @@ builttoroam
BVal
BValue
byapp
BYCOMMAND
BYPOSITION
CALCRECT
CALG

View File

@ -4,17 +4,52 @@
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
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<std::mutex> lock(m_callbacksMutex);
m_callbacks.push_back(callback);
}
void BugReportManager::clear_callbacks()
{
std::lock_guard<std::mutex> lock(m_callbacksMutex);
m_callbacks.clear();
}
void BugReportManager::notify_observers(bool isRunning)
{
std::lock_guard<std::mutex> 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();
}

View File

@ -1,3 +1,43 @@
#pragma once
void launch_bug_report() noexcept;
#include <functional>
#include <vector>
#include <mutex>
// Observer pattern for bug report status changes
using BugReportCallback = std::function<void(bool isRunning)>;
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<BugReportCallback> m_callbacks;
mutable std::mutex m_callbacksMutex;
};
// Legacy functions for backward compatibility
void launch_bug_report() noexcept;
bool is_bug_report_running() noexcept;

View File

@ -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)

View File

@ -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<HINSTANCE>(&__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<bool*>(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);
}
}
}

View File

@ -454,11 +454,25 @@
<Button x:Uid="GeneralPage_ViewDiagnosticDataViewerInfoButton" Click="Click_ViewDiagnosticDataViewerRestart" />
</InfoBar.ActionButton>
</InfoBar>
<tkcontrols:SettingsCard x:Uid="GeneralPage_ReportBugPackage" HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}">
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}">
<Button
x:Uid="GeneralPageReportBugPackage"
HorizontalAlignment="Right"
Click="BugReportToolClicked" />
Click="BugReportToolClicked"
IsEnabled="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<ProgressRing
Width="24"
Height="24"
HorizontalAlignment="Right"
VerticalAlignment="Center" />
</tkcontrols:SettingsCard>
</StackPanel>
</controls:SettingsGroup>

View File

@ -5,14 +5,15 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Flyout;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Data.Json;
namespace Microsoft.PowerToys.Settings.UI.Views
{
@ -87,6 +88,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
ViewModel.InitializeReportBugLink();
// Register IPC handler for bug report status
ShellPage.ShellHandler.IPCResponseHandleList.Add(HandleBugReportStatusResponse); // Register cleanup on unload
this.Unloaded += GeneralPage_Unloaded;
CheckBugReportStatus();
doRefreshBackupRestoreStatus(100);
}
@ -182,8 +189,48 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void BugReportToolClicked(object sender, RoutedEventArgs e)
{
// Start bug report
var launchPage = new LaunchPage();
launchPage.ReportBugBtn_Click(sender, e);
ViewModel.IsBugReportRunning = true;
// No need to start timer - the observer pattern will notify us when it finishes
}
private void CheckBugReportStatus()
{
// Send one-time request to check current bug report status
string ipcMessage = "{ \"bug_report_status\": { } }";
ShellPage.SendDefaultIPCMessage(ipcMessage);
}
private void HandleBugReportStatusResponse(JsonObject response)
{
if (response.ContainsKey("bug_report_running"))
{
var isRunning = response.GetNamedBoolean("bug_report_running");
// Update UI on the UI thread
this.DispatcherQueue.TryEnqueue(() =>
{
ViewModel.IsBugReportRunning = isRunning;
});
}
}
private void GeneralPage_Unloaded(object sender, RoutedEventArgs e)
{
CleanupBugReportHandlers();
}
private void CleanupBugReportHandlers()
{
// Remove IPC handler
if (ShellPage.ShellHandler?.IPCResponseHandleList != null)
{
ShellPage.ShellHandler.IPCResponseHandleList.Remove(HandleBugReportStatusResponse);
}
}
}
}

View File

@ -260,6 +260,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private bool _isNewVersionDownloading;
private bool _isNewVersionChecked;
private bool _isNoNetwork;
private bool _isBugReportRunning;
private bool _settingsBackupRestoreMessageVisible;
private string _settingsBackupMessage;
@ -952,6 +953,23 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool IsBugReportRunning
{
get
{
return _isBugReportRunning;
}
set
{
if (value != _isBugReportRunning)
{
_isBugReportRunning = value;
NotifyPropertyChanged();
}
}
}
public bool SettingsBackupRestoreMessageVisible
{
get