Qt: Invoke popup menus asynchronously

And use the invokable overload for addAction() while we're at it.
This commit is contained in:
Stenzek
2025-11-15 17:24:40 +10:00
parent b14c17b718
commit c025da4e5c
14 changed files with 271 additions and 284 deletions

View File

@@ -226,10 +226,9 @@ AdvancedSettingsWidget::~AdvancedSettingsWidget() = default;
void AdvancedSettingsWidget::onLogChannelsButtonClicked()
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
LogWindow::populateFilterMenu(&menu);
menu.exec(QCursor::pos());
QMenu* const menu = QtUtils::NewPopupMenu(this);
LogWindow::populateFilterMenu(menu);
menu->popup(QCursor::pos());
}
void AdvancedSettingsWidget::onAnyLogSinksChanged()

View File

@@ -262,33 +262,30 @@ void ControllerBindingWidget::onTypeChanged()
void ControllerBindingWidget::onAutomaticBindingClicked()
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* const menu = QtUtils::NewPopupMenu(this);
bool added = false;
for (const InputDeviceListModel::Device& dev : g_emu_thread->getInputDeviceListModel()->getDeviceList())
{
// we set it as data, because the device list could get invalidated while the menu is up
QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name));
action->setIcon(InputDeviceListModel::getIconForKey(dev.key));
action->setData(dev.identifier);
connect(action, &QAction::triggered, this,
[this, action]() { doDeviceAutomaticBinding(action->data().toString()); });
menu->addAction(InputDeviceListModel::getIconForKey(dev.key),
QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name),
[this, device = dev.identifier]() { doDeviceAutomaticBinding(device); });
added = true;
}
if (added)
{
QAction* action = menu.addAction(tr("Multiple devices..."));
connect(action, &QAction::triggered, this, &ControllerBindingWidget::onMultipleDeviceAutomaticBindingTriggered);
menu->addAction(tr("Multiple devices..."), this,
&ControllerBindingWidget::onMultipleDeviceAutomaticBindingTriggered);
}
else
{
QAction* action = menu.addAction(tr("No devices available"));
QAction* const action = menu->addAction(tr("No devices available"));
action->setEnabled(false);
}
menu.exec(QCursor::pos());
menu->popup(QCursor::pos());
}
void ControllerBindingWidget::onClearBindingsClicked()

View File

@@ -209,11 +209,9 @@ void DebuggerWindow::onBreakpointListContextMenuRequested()
const u32 address = item->data(1, Qt::UserRole).toUInt();
const CPU::BreakpointType type = static_cast<CPU::BreakpointType>(item->data(2, Qt::UserRole).toUInt());
QMenu menu(this);
QtUtils::StylePopupMenu(&menu);
connect(menu.addAction(tr("&Remove")), &QAction::triggered, this,
[this, address, type]() { removeBreakpoint(type, address); });
menu.exec(QCursor::pos());
QMenu* const menu = QtUtils::NewPopupMenu(this);
menu->addAction(tr("&Remove"), [this, address, type]() { removeBreakpoint(type, address); });
menu->popup(QCursor::pos());
}
void DebuggerWindow::onBreakpointListItemChanged(QTreeWidgetItem* item, int column)
@@ -295,31 +293,28 @@ void DebuggerWindow::onCodeViewContextMenuRequested(const QPoint& pt)
const VirtualMemoryAddress address = m_ui.codeView->getAddressAtPoint(pt);
m_ui.codeView->setSelectedAddress(address);
QMenu menu;
QtUtils::StylePopupMenu(&menu);
menu.addAction(QStringLiteral("0x%1").arg(static_cast<uint>(address), 8, 16, QChar('0')))->setEnabled(false);
menu.addSeparator();
QMenu* const menu = QtUtils::NewPopupMenu(this);
menu->addAction(QStringLiteral("0x%1").arg(static_cast<uint>(address), 8, 16, QChar('0')))->setEnabled(false);
menu->addSeparator();
QAction* action =
menu.addAction(QIcon::fromTheme(QStringLiteral("debug-toggle-breakpoint")), tr("Toggle &Breakpoint"));
connect(action, &QAction::triggered, this, [this, address]() { toggleBreakpoint(address); });
menu->addAction(QIcon::fromTheme(QStringLiteral("debug-toggle-breakpoint")), tr("Toggle &Breakpoint"),
[this, address]() { toggleBreakpoint(address); });
action = menu.addAction(QIcon::fromTheme(QStringLiteral("debugger-go-to-cursor")), tr("&Run To Cursor"));
connect(action, &QAction::triggered, this, [address]() {
menu->addAction(QIcon::fromTheme(QStringLiteral("debugger-go-to-cursor")), tr("&Run To Cursor"), [address]() {
Host::RunOnCPUThread([address]() {
CPU::AddBreakpoint(CPU::BreakpointType::Execute, address, true, true);
g_emu_thread->setSystemPaused(false);
});
});
menu.addSeparator();
action = menu.addAction(QIcon::fromTheme(QStringLiteral("debugger-go-to-address")), tr("View in &Dump"));
connect(action, &QAction::triggered, this, [this, address]() { scrollToMemoryAddress(address); });
menu->addSeparator();
menu->addAction(QIcon::fromTheme(QStringLiteral("debugger-go-to-address")), tr("View in &Dump"),
[this, address]() { scrollToMemoryAddress(address); });
action = menu.addAction(QIcon::fromTheme(QStringLiteral("debug-trace-line")), tr("&Follow Load/Store"));
connect(action, &QAction::triggered, this, [this, address]() { tryFollowLoadStore(address); });
menu->addAction(QIcon::fromTheme(QStringLiteral("debug-trace-line")), tr("&Follow Load/Store"),
[this, address]() { tryFollowLoadStore(address); });
menu.exec(m_ui.codeView->mapToGlobal(pt));
menu->popup(m_ui.codeView->mapToGlobal(pt));
}
void DebuggerWindow::onMemorySearchTriggered()

View File

@@ -344,27 +344,27 @@ void GameCheatSettingsWidget::onCheatListContextMenuRequested(const QPoint& pos)
Cheats::CodeInfo* selected = getSelectedCode();
const std::string selected_code = selected ? selected->name : std::string();
QMenu context_menu(m_ui.cheatList);
QtUtils::StylePopupMenu(&context_menu);
QMenu* context_menu = QtUtils::NewPopupMenu(m_ui.cheatList);
QAction* add = context_menu.addAction(QIcon::fromTheme(QStringLiteral("add-line")), tr("Add Cheat..."));
connect(add, &QAction::triggered, this, &GameCheatSettingsWidget::newCode);
QAction* edit = context_menu.addAction(QIcon::fromTheme(QStringLiteral("mag-line")), tr("Edit Cheat..."));
edit->setEnabled(selected != nullptr);
connect(edit, &QAction::triggered, this, [this, &selected_code]() { editCode(selected_code); });
QAction* remove = context_menu.addAction(QIcon::fromTheme(QStringLiteral("minus-line")), tr("Remove Cheat"));
remove->setEnabled(selected != nullptr);
connect(remove, &QAction::triggered, this, [this, &selected_code]() { removeCode(selected_code, true); });
context_menu.addSeparator();
context_menu->addAction(QIcon::fromTheme(QStringLiteral("add-line")), tr("Add Cheat..."), this,
&GameCheatSettingsWidget::newCode);
context_menu
->addAction(QIcon::fromTheme(QStringLiteral("mag-line")), tr("Edit Cheat..."),
[this, &selected_code]() { editCode(selected_code); })
->setEnabled(selected != nullptr);
context_menu
->addAction(QIcon::fromTheme(QStringLiteral("minus-line")), tr("Remove Cheat"),
[this, &selected_code]() { removeCode(selected_code, true); })
->setEnabled(selected != nullptr);
context_menu->addSeparator();
QAction* disable_all =
context_menu.addAction(QIcon::fromTheme(QStringLiteral("chat-off-line")), tr("Disable All Cheats"));
connect(disable_all, &QAction::triggered, this, &GameCheatSettingsWidget::disableAllCheats);
context_menu->addAction(QIcon::fromTheme(QStringLiteral("chat-off-line")), tr("Disable All Cheats"), this,
&GameCheatSettingsWidget::disableAllCheats);
QAction* reload = context_menu.addAction(QIcon::fromTheme(QStringLiteral("refresh-line")), tr("Reload Cheats"));
connect(reload, &QAction::triggered, this, &GameCheatSettingsWidget::onReloadClicked);
context_menu->addAction(QIcon::fromTheme(QStringLiteral("refresh-line")), tr("Reload Cheats"), this,
&GameCheatSettingsWidget::onReloadClicked);
context_menu.exec(m_ui.cheatList->mapToGlobal(pos));
context_menu->exec(m_ui.cheatList->mapToGlobal(pos));
}
void GameCheatSettingsWidget::onRemoveCodeClicked()
@@ -558,13 +558,10 @@ void GameCheatSettingsWidget::expandAllItems()
void GameCheatSettingsWidget::onImportClicked()
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
connect(menu.addAction(tr("From File...")), &QAction::triggered, this,
&GameCheatSettingsWidget::onImportFromFileTriggered);
connect(menu.addAction(tr("From Text...")), &QAction::triggered, this,
&GameCheatSettingsWidget::onImportFromTextTriggered);
menu.exec(QCursor::pos());
QMenu* const menu = QtUtils::NewPopupMenu(this);
menu->addAction(tr("From File..."), this, &GameCheatSettingsWidget::onImportFromFileTriggered);
menu->addAction(tr("From Text..."), this, &GameCheatSettingsWidget::onImportFromTextTriggered);
menu->exec(QCursor::pos());
}
void GameCheatSettingsWidget::onImportFromFileTriggered()

View File

@@ -161,17 +161,16 @@ void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& p
const int row = selection[0].row();
QMenu menu;
QtUtils::StylePopupMenu(&menu);
menu.addAction(QIcon::fromTheme("folder-reduce-line"), tr("Remove"), this,
&GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked);
menu.addSeparator();
menu.addAction(QIcon::fromTheme("folder-open-line"), tr("Open Directory..."), [this, row]() {
QMenu* const menu = QtUtils::NewPopupMenu(this);
menu->addAction(QIcon::fromTheme("folder-reduce-line"), tr("Remove"), this,
&GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked);
menu->addSeparator();
menu->addAction(QIcon::fromTheme("folder-open-line"), tr("Open Directory..."), [this, row]() {
const QTreeWidgetItem* const item = m_ui.searchDirectoryList->topLevelItem(row);
if (item)
QtUtils::OpenURL(this, QUrl::fromLocalFile(item->text(0)));
});
menu.exec(m_ui.searchDirectoryList->mapToGlobal(point));
menu->popup(m_ui.searchDirectoryList->mapToGlobal(point));
}
void GameListSettingsWidget::addSearchDirectory(QWidget* parent_widget)

View File

@@ -1920,7 +1920,9 @@ void GameListWidget::updateBackground(bool reload_image)
}
QImage scaled_image = m_background_image;
resizeAndPadImage(&scaled_image, QtUtils::ApplyDevicePixelRatioToSize(m_ui.stack->size(), m_model->getDevicePixelRatio()), true, true);
resizeAndPadImage(&scaled_image,
QtUtils::ApplyDevicePixelRatioToSize(m_ui.stack->size(), m_model->getDevicePixelRatio()), true,
true);
QPalette new_palette = qApp->palette(m_ui.stack);
new_palette.setBrush(QPalette::Window, QPixmap::fromImage(scaled_image));
@@ -2484,21 +2486,20 @@ void GameListListView::setAndSaveColumnHidden(int column, bool hidden)
void GameListListView::onHeaderContextMenuRequested(const QPoint& point)
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* menu = QtUtils::NewPopupMenu(this);
for (int column = 0; column < GameListModel::Column_Count; column++)
{
if (column == GameListModel::Column_Cover)
continue;
QAction* const action = menu.addAction(m_model->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString());
QAction* const action = menu->addAction(m_model->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString(),
[this, column](bool enabled) { setAndSaveColumnHidden(column, !enabled); });
action->setCheckable(true);
action->setChecked(!isColumnHidden(column));
connect(action, &QAction::triggered, [this, column](bool enabled) { setAndSaveColumnHidden(column, !enabled); });
}
menu.exec(mapToGlobal(point));
menu->exec(mapToGlobal(point));
}
void GameListListView::adjustIconSize(int delta)

View File

@@ -154,27 +154,26 @@ void ISOBrowserWindow::onFileContextMenuRequested(const QPoint& pos)
if (items.isEmpty())
return;
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* const menu = QtUtils::NewPopupMenu(this);
const bool is_directory = items.front()->data(0, Qt::UserRole + 1).toBool();
const QString path = items.front()->data(0, Qt::UserRole).toString();
if (is_directory)
{
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::FolderOpen), tr("&Open")), &QAction::triggered, this,
[this, &path]() { populateFiles(path); });
menu->addAction(QIcon::fromTheme(QIcon::ThemeIcon::FolderOpen), tr("&Open"),
[this, path]() { populateFiles(path); });
}
else
{
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("&Extract")), &QAction::triggered,
this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Data); });
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&XA)")),
&QAction::triggered, this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Mode2); });
connect(menu.addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&Raw)")),
&QAction::triggered, this, [this, &path]() { extractFile(path, IsoReader::ReadMode::Raw); });
menu->addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("&Extract"),
[this, path]() { extractFile(path, IsoReader::ReadMode::Data); });
menu->addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&XA)"),
[this, path]() { extractFile(path, IsoReader::ReadMode::Mode2); });
menu->addAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSaveAs), tr("Extract (&Raw)"),
[this, path]() { extractFile(path, IsoReader::ReadMode::Raw); });
}
menu.exec(m_ui.fileView->mapToGlobal(pos));
menu->popup(m_ui.fileView->mapToGlobal(pos));
}
void ISOBrowserWindow::extractFile(const QString& path, IsoReader::ReadMode mode)

View File

@@ -227,14 +227,13 @@ void LogWindow::populateFilterMenu(QMenu* filter_menu)
for (const char* channel_name : Log::GetChannelNames())
{
const bool enabled = si->GetBoolValue("Logging", channel_name, true);
QAction* action = filter_menu->addAction(QString::fromUtf8(channel_name));
action->setCheckable(true);
action->setChecked(enabled);
connect(action, &QAction::triggered, action, [channel_name](bool checked) {
QAction* const action = filter_menu->addAction(QString::fromUtf8(channel_name), [channel_name](bool checked) {
Host::SetBaseBoolSettingValue("Logging", channel_name, checked);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings(false);
});
action->setCheckable(true);
action->setChecked(enabled);
}
}

View File

@@ -909,6 +909,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg
load_state_menu = menu->addMenu(tr("Load State"));
load_state_menu->setEnabled(false);
QtUtils::StylePopupMenu(load_state_menu);
if (!entry->serial.empty())
{
@@ -1109,11 +1110,11 @@ void MainWindow::onCheatsMenuAboutToShow()
}
QMenu* apply_submenu = menu->addMenu(tr("&Apply Cheat"));
QtUtils::StylePopupMenu(apply_submenu);
for (const QString& name : names)
{
const QAction* action = apply_submenu->addAction(name);
connect(action, &QAction::triggered, apply_submenu, [action]() {
Host::RunOnCPUThread([name = action->text().toStdString()]() {
apply_submenu->addAction(name, [name]() {
Host::RunOnCPUThread([name = name.toStdString()]() {
if (System::IsValid())
Cheats::ApplyManualCode(name);
});
@@ -1633,144 +1634,143 @@ void MainWindow::onGameListEntryActivated()
void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* const menu = QtUtils::NewPopupMenu(this);
const auto lock = GameList::GetLock();
const GameList::Entry* entry = m_game_list_widget->getSelectedEntry();
if (entry)
{
const auto lock = GameList::GetLock();
const GameList::Entry* entry = m_game_list_widget->getSelectedEntry();
const QString qpath = QString::fromStdString(entry->path); // QString is CoW
if (entry)
if (!entry->IsDiscSet())
{
const QString qpath = QString::fromStdString(entry->path); // QString is CoW
menu->addAction(tr("Properties..."), [qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (!entry || !g_main_window)
return;
if (!entry->IsDiscSet())
SettingsWindow::openGamePropertiesDialog(entry);
});
menu->addAction(tr("Open Containing Directory..."), [this, qpath]() {
const QFileInfo fi(qpath);
QtUtils::OpenURL(this, QUrl::fromLocalFile(fi.absolutePath()));
});
if (entry->IsDisc())
{
connect(menu.addAction(tr("Properties...")), &QAction::triggered, [qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (!entry || !g_main_window)
return;
SettingsWindow::openGamePropertiesDialog(entry);
});
connect(menu.addAction(tr("Open Containing Directory...")), &QAction::triggered, [this, qpath]() {
const QFileInfo fi(qpath);
QtUtils::OpenURL(this, QUrl::fromLocalFile(fi.absolutePath()));
});
if (entry->IsDisc())
{
connect(menu.addAction(tr("Browse ISO...")), &QAction::triggered, [this, qpath]() {
ISOBrowserWindow* ib = ISOBrowserWindow::createAndOpenFile(this, qpath);
if (ib)
{
ib->setAttribute(Qt::WA_DeleteOnClose);
ib->show();
}
});
}
connect(menu.addAction(tr("Set Cover Image...")), &QAction::triggered, [this, qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (entry)
setGameListEntryCoverImage(entry);
});
menu.addSeparator();
if (!s_system_valid)
{
populateGameListContextMenu(entry, this, &menu);
menu.addSeparator();
connect(menu.addAction(tr("Default Boot")), &QAction::triggered,
[this, qpath]() mutable { g_emu_thread->bootSystem(getSystemBootParameters(qpath.toStdString())); });
connect(menu.addAction(tr("Fast Boot")), &QAction::triggered, [this, qpath]() mutable {
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_fast_boot = true;
g_emu_thread->bootSystem(std::move(boot_params));
});
connect(menu.addAction(tr("Full Boot")), &QAction::triggered, [this, qpath]() mutable {
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_fast_boot = false;
g_emu_thread->bootSystem(std::move(boot_params));
});
if (m_ui.menuDebug->menuAction()->isVisible())
menu->addAction(tr("Browse ISO..."), [this, qpath]() {
ISOBrowserWindow* ib = ISOBrowserWindow::createAndOpenFile(this, qpath);
if (ib)
{
connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, qpath]() mutable {
openCPUDebugger();
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_start_paused = true;
boot_params->disable_achievements_hardcore_mode = true;
g_emu_thread->bootSystem(std::move(boot_params));
});
ib->setAttribute(Qt::WA_DeleteOnClose);
ib->show();
}
}
else
});
}
menu->addAction(tr("Set Cover Image..."), [this, qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (entry)
setGameListEntryCoverImage(entry);
});
menu->addSeparator();
if (!s_system_valid)
{
populateGameListContextMenu(entry, this, menu);
menu->addSeparator();
menu->addAction(tr("Default Boot"), [this, qpath]() mutable {
g_emu_thread->bootSystem(getSystemBootParameters(qpath.toStdString()));
});
menu->addAction(tr("Fast Boot"), [this, qpath]() mutable {
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_fast_boot = true;
g_emu_thread->bootSystem(std::move(boot_params));
});
menu->addAction(tr("Full Boot"), [this, qpath]() mutable {
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_fast_boot = false;
g_emu_thread->bootSystem(std::move(boot_params));
});
if (m_ui.menuDebug->menuAction()->isVisible())
{
connect(menu.addAction(tr("Change Disc")), &QAction::triggered, [this, qpath]() {
g_emu_thread->changeDisc(qpath, false, true);
g_emu_thread->setSystemPaused(false);
switchToEmulationView();
menu->addAction(tr("Boot and Debug"), [this, qpath]() mutable {
openCPUDebugger();
std::shared_ptr<SystemBootParameters> boot_params = getSystemBootParameters(qpath.toStdString());
boot_params->override_start_paused = true;
boot_params->disable_achievements_hardcore_mode = true;
g_emu_thread->bootSystem(std::move(boot_params));
});
}
}
else
{
connect(menu.addAction(tr("Properties...")), &QAction::triggered, [dsentry = entry->GetDiscSetEntry()]() {
// resolve path first
auto lock = GameList::GetLock();
const GameList::Entry* first_disc = GameList::GetFirstDiscSetMember(dsentry);
if (first_disc && g_main_window)
SettingsWindow::openGamePropertiesDialog(first_disc);
menu->addAction(tr("Change Disc"), [this, qpath]() {
g_emu_thread->changeDisc(qpath, false, true);
g_emu_thread->setSystemPaused(false);
switchToEmulationView();
});
connect(menu.addAction(tr("Set Cover Image...")), &QAction::triggered, [this, qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (!entry)
return;
setGameListEntryCoverImage(entry);
});
menu.addSeparator();
populateGameListContextMenu(entry, this, &menu);
menu.addSeparator();
connect(menu.addAction(tr("Select Disc...")), &QAction::triggered, this, &MainWindow::onGameListEntryActivated);
}
}
else
{
menu->addAction(tr("Properties..."), [dsentry = entry->GetDiscSetEntry()]() {
// resolve path first
auto lock = GameList::GetLock();
const GameList::Entry* first_disc = GameList::GetFirstDiscSetMember(dsentry);
if (first_disc && g_main_window)
SettingsWindow::openGamePropertiesDialog(first_disc);
});
menu.addSeparator();
connect(menu.addAction(tr("Exclude From List")), &QAction::triggered,
[this, qpath]() { getSettingsWindow()->getGameListSettingsWidget()->addExcludedPath(qpath); });
connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, qpath]() {
menu->addAction(tr("Set Cover Image..."), [this, qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (!entry)
return;
clearGameListEntryPlayTime(entry);
setGameListEntryCoverImage(entry);
});
menu->addSeparator();
populateGameListContextMenu(entry, this, menu);
menu->addSeparator();
menu->addAction(tr("Select Disc..."), this, &MainWindow::onGameListEntryActivated);
}
menu.addSeparator();
menu->addSeparator();
connect(menu.addAction(tr("Add Search Directory...")), &QAction::triggered,
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
menu->addAction(tr("Exclude From List"),
[this, qpath]() { getSettingsWindow()->getGameListSettingsWidget()->addExcludedPath(qpath); });
menu->addAction(tr("Reset Play Time"), [this, qpath]() {
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(qpath.toStdString());
if (!entry)
return;
clearGameListEntryPlayTime(entry);
});
}
menu.exec(point);
menu->addSeparator();
menu->addAction(tr("Add Search Directory..."),
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
menu->exec(point);
}
void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
@@ -2112,55 +2112,53 @@ void MainWindow::onToolbarContextMenuRequested(const QPoint& pos)
std::vector<std::string_view> active_buttons = StringUtil::SplitString(active_buttons_str, ',');
bool active_buttons_changed = false;
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* const menu = QtUtils::NewPopupMenu(this);
QAction* action = menu.addAction(tr("Lock Toolbar"));
QAction* action = menu->addAction(tr("Lock Toolbar"));
action->setCheckable(true);
action->setChecked(!m_ui.toolBar->isMovable());
connect(action, &QAction::toggled, this, &MainWindow::onViewToolbarLockActionToggled);
action = menu.addAction(tr("Small Icons"));
action = menu->addAction(tr("Small Icons"));
action->setCheckable(true);
action->setChecked(Host::GetBaseBoolSettingValue("UI", "ToolbarSmallIcons", false));
connect(action, &QAction::toggled, this, &MainWindow::onViewToolbarSmallIconsActionToggled);
action = menu.addAction(tr("Show Labels"));
action = menu->addAction(tr("Show Labels"));
action->setCheckable(true);
action->setChecked(Host::GetBaseBoolSettingValue("UI", "ToolbarLabels", true));
connect(action, &QAction::toggled, this, &MainWindow::onViewToolbarLabelsActionToggled);
action = menu.addAction(tr("Labels Beside Icons"));
action = menu->addAction(tr("Labels Beside Icons"));
action->setCheckable(true);
action->setChecked(Host::GetBaseBoolSettingValue("UI", "ToolbarLabelsBesideIcons", false));
action->setEnabled(show_labels);
connect(action, &QAction::toggled, this, &MainWindow::onViewToolbarLabelsBesideIconsActionToggled);
QMenu* position_menu = menu.addMenu(tr("Position"));
QMenu* position_menu = menu->addMenu(tr("Position"));
QtUtils::StylePopupMenu(position_menu);
for (const auto& [area, name] : s_toolbar_areas)
{
QAction* position_action = position_menu->addAction(tr(name));
position_action->setCheckable(true);
position_action->setChecked(toolBarArea(m_ui.toolBar) == area);
connect(position_action, &QAction::triggered, this, [this, name]() {
QAction* const position_action = position_menu->addAction(tr(name), [this, name]() {
Host::SetBaseStringSettingValue("UI", "ToolbarArea", name);
Host::CommitBaseSettingChanges();
updateToolbarArea();
});
position_action->setCheckable(true);
position_action->setChecked(toolBarArea(m_ui.toolBar) == area);
}
menu.addSeparator();
menu->addSeparator();
for (const auto& [name, action_ptr] : s_toolbar_actions)
{
if (!name)
{
menu.addSeparator();
menu->addSeparator();
continue;
}
QAction* menu_action = menu.addAction((m_ui.*action_ptr)->text());
QAction* menu_action = menu->addAction((m_ui.*action_ptr)->text());
menu_action->setCheckable(true);
menu_action->setChecked(StringUtil::IsInStringList(active_buttons, name));
connect(menu_action, &QAction::toggled, this, [&active_buttons, &active_buttons_changed, name](bool checked) {
@@ -2172,7 +2170,7 @@ void MainWindow::onToolbarContextMenuRequested(const QPoint& pos)
});
}
menu.exec(m_ui.toolBar->mapToGlobal(pos));
menu->popup(m_ui.toolBar->mapToGlobal(pos));
if (active_buttons_changed)
{

View File

@@ -810,24 +810,19 @@ void MemoryCardEditorWindow::onCardContextMenuRequested(const QPoint& pos)
if (!card)
return;
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QAction* action = menu.addAction(tr("Delete File"));
QMenu* const menu = QtUtils::NewPopupMenu(this);
QAction* action = menu->addAction(tr("Delete File"), this, &MemoryCardEditorWindow::doDeleteFile);
action->setEnabled(fi && !fi->deleted);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doDeleteFile);
action = menu.addAction(tr("Undelete File"));
action = menu->addAction(tr("Undelete File"), this, &MemoryCardEditorWindow::doUndeleteFile);
action->setEnabled(fi && fi->deleted);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doUndeleteFile);
action = menu.addAction(tr("Rename File"));
action = menu->addAction(tr("Rename File"), this, &MemoryCardEditorWindow::doRenameSaveFile);
action->setEnabled(fi != nullptr);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doRenameSaveFile);
action = menu.addAction(tr("Export File"));
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doExportSaveFile);
action = menu.addAction(tr("Copy File"));
action = menu->addAction(tr("Export File"), this, &MemoryCardEditorWindow::doExportSaveFile);
action->setEnabled(fi != nullptr);
action = menu->addAction(tr("Copy File"), this, &MemoryCardEditorWindow::doCopyFile);
action->setEnabled(fi && !m_card_a.filename.empty() && !m_card_b.filename.empty());
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doCopyFile);
menu.exec(table->mapToGlobal(pos));
menu->exec(table->mapToGlobal(pos));
}
std::tuple<MemoryCardEditorWindow::Card*, const MemoryCardImage::FileInfo*> MemoryCardEditorWindow::getSelectedFile()

View File

@@ -311,6 +311,21 @@ void QtUtils::StyleChildMenus(QWidget* widget)
StylePopupMenu(menu);
}
QMenu* QtUtils::NewPopupMenu(QWidget* parent, bool delete_on_close /*= true*/)
{
QMenu* menu = new QMenu(parent);
if (QtHost::IsStyleSheetApplicationTheme())
{
menu->setWindowFlags(menu->windowFlags() | Qt::NoDropShadowWindowHint | Qt::FramelessWindowHint);
menu->setAttribute(Qt::WA_TranslucentBackground, true);
}
if (delete_on_close)
menu->setAttribute(Qt::WA_DeleteOnClose, true);
return menu;
}
QMessageBox::StandardButton QtUtils::MessageBoxInformation(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)

View File

@@ -133,6 +133,9 @@ QMessageBox* NewMessageBox(QMessageBox::Icon icon, const QString& title, const Q
void StylePopupMenu(QMenu* menu);
void StyleChildMenus(QWidget* widget);
/// Creates a new popup menu, styled for the current theme.
QMenu* NewPopupMenu(QWidget* parent, bool delete_on_close = true);
/// Returns icon for language.
QIcon GetIconForTranslationLanguage(std::string_view language_name);

View File

@@ -385,15 +385,14 @@ struct SettingAccessor<QSlider>
{
widget->setContextMenuPolicy(Qt::CustomContextMenu);
widget->connect(widget, &QSlider::customContextMenuRequested, widget, [widget, func](const QPoint& pt) {
QMenu menu(widget);
widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget,
[widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableIntValue(widget, std::nullopt);
widget->blockSignals(old);
func();
});
menu.exec(widget->mapToGlobal(pt));
QMenu* const menu = QtUtils::NewPopupMenu(widget);
menu->addAction(qApp->translate("SettingWidgetBinder", "Reset"), [widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableIntValue(widget, std::nullopt);
widget->blockSignals(old);
func();
});
menu->popup(widget->mapToGlobal(pt));
});
widget->connect(widget, &QSlider::valueChanged, widget, [widget, func = std::move(func)]() {
if (widget->property(IS_NULL_PROPERTY).toBool())
@@ -518,16 +517,15 @@ struct SettingAccessor<QSpinBox>
{
widget->setContextMenuPolicy(Qt::CustomContextMenu);
widget->connect(widget, &QSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) mutable {
QMenu menu(widget);
widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget,
[widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableIntValue(widget, std::nullopt);
widget->blockSignals(old);
updateFont(widget, true);
func();
});
menu.exec(widget->mapToGlobal(pt));
QMenu* const menu = QtUtils::NewPopupMenu(widget);
menu->addAction(qApp->translate("SettingWidgetBinder", "Reset"), [widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableIntValue(widget, std::nullopt);
widget->blockSignals(old);
updateFont(widget, true);
func();
});
menu->exec(widget->mapToGlobal(pt));
});
widget->connect(widget, &QSpinBox::valueChanged, widget, [widget, func = std::move(func)]() {
if (widget->property(IS_NULL_PROPERTY).toBool())
@@ -655,16 +653,15 @@ struct SettingAccessor<QDoubleSpinBox>
{
widget->setContextMenuPolicy(Qt::CustomContextMenu);
widget->connect(widget, &QDoubleSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) {
QMenu menu(widget);
widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget,
[widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableFloatValue(widget, std::nullopt);
widget->blockSignals(old);
updateFont(widget, true);
func();
});
menu.exec(widget->mapToGlobal(pt));
QMenu* const menu = QtUtils::NewPopupMenu(widget);
menu->addAction(qApp->translate("SettingWidgetBinder", "Reset"), [widget, func = std::move(func)]() {
const bool old = widget->blockSignals(true);
setNullableFloatValue(widget, std::nullopt);
widget->blockSignals(old);
updateFont(widget, true);
func();
});
menu->popup(widget->mapToGlobal(pt));
});
widget->connect(widget, QOverload<double>::of(&QDoubleSpinBox::valueChanged), widget,
[widget, func = std::move(func)]() {

View File

@@ -267,14 +267,13 @@ void SetupWizardDialog::onDirectoryListContextMenuRequested(const QPoint& point)
const int row = selection[0].row();
QMenu menu;
QtUtils::StylePopupMenu(&menu);
menu.addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); });
menu.addSeparator();
menu.addAction(tr("Open Directory..."), [this, row]() {
QMenu* const menu = QtUtils::NewPopupMenu(this);
menu->addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); });
menu->addSeparator();
menu->addAction(tr("Open Directory..."), [this, row]() {
QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectoryList->item(row, 0)->text()));
});
menu.exec(m_ui.searchDirectoryList->mapToGlobal(point));
menu->popup(m_ui.searchDirectoryList->mapToGlobal(point));
}
void SetupWizardDialog::onAddSearchDirectoryButtonClicked()
@@ -284,12 +283,12 @@ void SetupWizardDialog::onAddSearchDirectoryButtonClicked()
if (dir.isEmpty())
return;
QMessageBox::StandardButton selection =
QtUtils::MessageBoxQuestion(this, tr("Scan Recursively?"),
tr("Would you like to scan the directory \"%1\" recursively?\n\nScanning recursively takes "
"more time, but will identify files in subdirectories.")
.arg(dir),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
QMessageBox::StandardButton selection = QtUtils::MessageBoxQuestion(
this, tr("Scan Recursively?"),
tr("Would you like to scan the directory \"%1\" recursively?\n\nScanning recursively takes "
"more time, but will identify files in subdirectories.")
.arg(dir),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (selection != QMessageBox::Yes && selection != QMessageBox::No)
return;
@@ -437,35 +436,29 @@ QString SetupWizardDialog::findCurrentDeviceForPort(u32 port) const
void SetupWizardDialog::openAutomaticMappingMenu(u32 port, QLabel* update_label)
{
QMenu menu;
QtUtils::StylePopupMenu(&menu);
QMenu* const menu = QtUtils::NewPopupMenu(this);
bool added = false;
for (const InputDeviceListModel::Device& dev : g_emu_thread->getInputDeviceListModel()->getDeviceList())
{
// we set it as data, because the device list could get invalidated while the menu is up
QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name));
action->setIcon(InputDeviceListModel::getIconForKey(dev.key));
action->setData(dev.identifier);
connect(action, &QAction::triggered, this, [this, port, update_label, action]() {
doDeviceAutomaticBinding(port, update_label, action->data().toString());
});
menu->addAction(
InputDeviceListModel::getIconForKey(dev.key), QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name),
[this, port, update_label, device = dev.identifier]() { doDeviceAutomaticBinding(port, update_label, device); });
added = true;
}
if (added)
{
QAction* action = menu.addAction(tr("Multiple Devices..."));
connect(action, &QAction::triggered, this,
[this, port, update_label]() { doMultipleDeviceAutomaticBinding(port, update_label); });
menu->addAction(tr("Multiple Devices..."),
[this, port, update_label]() { doMultipleDeviceAutomaticBinding(port, update_label); });
}
else
{
QAction* action = menu.addAction(tr("No devices available"));
action->setEnabled(false);
menu->addAction(tr("No devices available"))->setEnabled(false);
}
menu.exec(QCursor::pos());
menu->popup(QCursor::pos());
}
void SetupWizardDialog::doDeviceAutomaticBinding(u32 port, QLabel* update_label, const QString& device)