Debugger: Extract custom menu bar as its own class

This commit is contained in:
chaoticgd 2025-03-21 20:37:31 +00:00 committed by lightningterror
parent 76f8ffeb90
commit 47657b51ab
8 changed files with 436 additions and 305 deletions

View File

@ -196,6 +196,8 @@ target_sources(pcsx2-qt PRIVATE
Debugger/Docking/DockLayout.h Debugger/Docking/DockLayout.h
Debugger/Docking/DockManager.cpp Debugger/Docking/DockManager.cpp
Debugger/Docking/DockManager.h Debugger/Docking/DockManager.h
Debugger/Docking/DockMenuBar.cpp
Debugger/Docking/DockMenuBar.h
Debugger/Docking/DockTables.cpp Debugger/Docking/DockTables.cpp
Debugger/Docking/DockTables.h Debugger/Docking/DockTables.h
Debugger/Docking/DockUtils.cpp Debugger/Docking/DockUtils.cpp

View File

@ -112,7 +112,7 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
QMenuBar* menu_bar = menuBar(); QMenuBar* menu_bar = menuBar();
setMenuWidget(m_dock_manager->createLayoutSwitcher(menu_bar)); setMenuWidget(m_dock_manager->createMenuBar(menu_bar));
Host::RunOnCPUThread([]() { Host::RunOnCPUThread([]() {
R5900SymbolImporter.OnDebuggerOpened(); R5900SymbolImporter.OnDebuggerOpened();

View File

@ -25,7 +25,6 @@
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QtTranslation> #include <QtCore/QtTranslation>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <QtWidgets/QPushButton>
DockManager::DockManager(QObject* parent) DockManager::DockManager(QObject* parent)
: QObject(parent) : QObject(parent)
@ -33,9 +32,6 @@ DockManager::DockManager(QObject* parent)
QTimer* autosave_timer = new QTimer(this); QTimer* autosave_timer = new QTimer(this);
connect(autosave_timer, &QTimer::timeout, this, &DockManager::saveCurrentLayout); connect(autosave_timer, &QTimer::timeout, this, &DockManager::saveCurrentLayout);
autosave_timer->start(60 * 1000); autosave_timer->start(60 * 1000);
m_blink_timer = new QTimer(this);
connect(m_blink_timer, &QTimer::timeout, this, &DockManager::layoutSwitcherUpdateBlink);
} }
void DockManager::configureDockingSystem() void DockManager::configureDockingSystem()
@ -144,17 +140,13 @@ void DockManager::switchToLayout(DockLayout::Index layout_index, bool blink_tab)
layout.thaw(); layout.thaw();
int tab_index = static_cast<int>(layout_index); int tab_index = static_cast<int>(layout_index);
if (m_switcher && tab_index >= 0 && tab_index < m_plus_tab_index) if (m_menu_bar && tab_index >= 0)
{ m_menu_bar->onCurrentLayoutChanged(layout_index);
m_ignore_current_tab_changed = true;
m_switcher->setCurrentIndex(tab_index);
m_ignore_current_tab_changed = false;
}
} }
} }
if (blink_tab) if (blink_tab)
layoutSwitcherStartBlink(); m_menu_bar->startBlink(m_current_layout);
} }
bool DockManager::switchToLayoutWithCPU(BreakPointCpu cpu, bool blink_tab) bool DockManager::switchToLayoutWithCPU(BreakPointCpu cpu, bool blink_tab)
@ -476,103 +468,43 @@ void DockManager::createWindowsMenu(QMenu* menu)
menu->addAction(toggle.action); menu->addAction(toggle.action);
} }
QWidget* DockManager::createLayoutSwitcher(QWidget* menu_bar) QWidget* DockManager::createMenuBar(QWidget* original_menu_bar)
{ {
QWidget* container = new QWidget; pxAssert(!m_menu_bar);
QHBoxLayout* layout = new QHBoxLayout; m_menu_bar = new DockMenuBar(original_menu_bar);
layout->setContentsMargins(0, 2, 2, 0);
container->setLayout(layout);
QWidget* menu_wrapper = new QWidget; connect(m_menu_bar, &DockMenuBar::currentLayoutChanged, this, [this](DockLayout::Index layout_index) {
menu_wrapper->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); if (layout_index >= m_layouts.size())
layout->addWidget(menu_wrapper); return;
QHBoxLayout* menu_layout = new QHBoxLayout; switchToLayout(layout_index);
menu_layout->setContentsMargins(0, 4, 0, 4); });
menu_wrapper->setLayout(menu_layout); connect(m_menu_bar, &DockMenuBar::newButtonClicked, this, &DockManager::newLayoutClicked);
connect(m_menu_bar, &DockMenuBar::layoutMoved, this, &DockManager::layoutSwitcherTabMoved);
menu_layout->addWidget(menu_bar); connect(m_menu_bar, &DockMenuBar::lockButtonToggled, this, &DockManager::setLayoutLockedAndSaveSetting);
connect(m_menu_bar, &DockMenuBar::layoutSwitcherContextMenuRequested,
m_switcher = new QTabBar; this, &DockManager::openLayoutSwitcherContextMenu);
m_switcher->setContentsMargins(0, 0, 0, 0);
m_switcher->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
m_switcher->setContextMenuPolicy(Qt::CustomContextMenu);
m_switcher->setMovable(true);
layout->addWidget(m_switcher);
updateLayoutSwitcher(); updateLayoutSwitcher();
connect(m_switcher, &QTabBar::tabMoved, this, &DockManager::layoutSwitcherTabMoved);
connect(m_switcher, &QTabBar::customContextMenuRequested, this, &DockManager::layoutSwitcherContextMenu);
QWidget* spacer = new QWidget;
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
layout->addWidget(spacer);
bool layout_locked = Host::GetBaseBoolSettingValue("Debugger/UserInterface", "LayoutLocked", true); bool layout_locked = Host::GetBaseBoolSettingValue("Debugger/UserInterface", "LayoutLocked", true);
setLayoutLocked(layout_locked, false);
QPushButton* lock_layout_toggle = new QPushButton; return m_menu_bar;
lock_layout_toggle->setCheckable(true);
lock_layout_toggle->setChecked(layout_locked);
lock_layout_toggle->setFlat(true);
connect(lock_layout_toggle, &QPushButton::toggled, this, [this, lock_layout_toggle](bool checked) {
setLayoutLocked(checked, lock_layout_toggle, true);
});
layout->addWidget(lock_layout_toggle);
setLayoutLocked(layout_locked, lock_layout_toggle, false);
return container;
} }
void DockManager::updateLayoutSwitcher() void DockManager::updateLayoutSwitcher()
{ {
if (!m_switcher) if (m_menu_bar)
return; m_menu_bar->updateLayoutSwitcher(m_current_layout, m_layouts);
disconnect(m_tab_connection);
for (int i = m_switcher->count(); i > 0; i--)
m_switcher->removeTab(i - 1);
for (DockLayout& layout : m_layouts)
{
const char* cpu_name = DebugInterface::cpuName(layout.cpu());
QString tab_name = QString("%1 (%2)").arg(layout.name()).arg(cpu_name);
m_switcher->addTab(tab_name);
}
m_plus_tab_index = m_switcher->addTab("+");
m_current_tab_index = m_current_layout;
if (m_current_layout != DockLayout::INVALID_INDEX)
m_switcher->setCurrentIndex(m_current_layout);
// If we don't have any layouts, the currently selected tab will never be
// changed, so we respond to all clicks instead.
if (!m_layouts.empty())
m_tab_connection = connect(m_switcher, &QTabBar::currentChanged, this, &DockManager::layoutSwitcherTabChanged);
else
m_tab_connection = connect(m_switcher, &QTabBar::tabBarClicked, this, &DockManager::layoutSwitcherTabChanged);
layoutSwitcherStopBlink();
} }
void DockManager::layoutSwitcherTabChanged(int index) void DockManager::newLayoutClicked()
{ {
// Prevent recursion. // The plus button has just been made the current tab, so set it back to the
if (m_ignore_current_tab_changed) // one corresponding to the current layout again.
return; m_menu_bar->onCurrentLayoutChanged(m_current_layout);
if (index == m_plus_tab_index)
{
if (m_current_tab_index >= 0 && m_current_tab_index < m_plus_tab_index)
{
m_ignore_current_tab_changed = true;
m_switcher->setCurrentIndex(m_current_tab_index);
m_ignore_current_tab_changed = false;
}
auto name_validator = [this](const QString& name) { auto name_validator = [this](const QString& name) {
return !hasNameConflict(name, DockLayout::INVALID_INDEX); return !hasNameConflict(name, DockLayout::INVALID_INDEX);
@ -604,7 +536,7 @@ void DockManager::layoutSwitcherTabChanged(int index)
case LayoutEditorDialog::CLONE_LAYOUT: case LayoutEditorDialog::CLONE_LAYOUT:
{ {
if (m_current_layout == DockLayout::INVALID_INDEX) if (m_current_layout == DockLayout::INVALID_INDEX)
return; break;
DockLayout::Index old_layout = m_current_layout; DockLayout::Index old_layout = m_current_layout;
@ -616,63 +548,48 @@ void DockManager::layoutSwitcherTabChanged(int index)
} }
} }
if (new_layout != DockLayout::INVALID_INDEX)
{
updateLayoutSwitcher(); updateLayoutSwitcher();
switchToLayout(new_layout); switchToLayout(new_layout);
} }
}
delete dialog.get(); delete dialog.get();
}
else
{
DockLayout::Index layout_index = static_cast<DockLayout::Index>(index);
if (layout_index < 0 || layout_index >= m_layouts.size())
return;
switchToLayout(layout_index);
m_current_tab_index = index;
}
} }
void DockManager::layoutSwitcherTabMoved(int from, int to) void DockManager::openLayoutSwitcherContextMenu(const QPoint& pos, QTabBar* layout_switcher)
{ {
DockLayout::Index from_index = static_cast<DockLayout::Index>(from); DockLayout::Index layout_index = static_cast<DockLayout::Index>(layout_switcher->tabAt(pos));
DockLayout::Index to_index = static_cast<DockLayout::Index>(to);
if (from_index >= m_layouts.size() || to_index >= m_layouts.size())
{
// This happens when the user tries to move a layout to the right of the
// plus button.
updateLayoutSwitcher();
return;
}
DockLayout& from_layout = m_layouts[from_index];
DockLayout& to_layout = m_layouts[to_index];
std::swap(from_layout, to_layout);
from_layout.save(from_index);
to_layout.save(to_index);
if (from_index == m_current_layout)
m_current_layout = to_index;
else if (to_index == m_current_layout)
m_current_layout = from_index;
}
void DockManager::layoutSwitcherContextMenu(QPoint pos)
{
DockLayout::Index layout_index = static_cast<DockLayout::Index>(m_switcher->tabAt(pos));
if (layout_index >= m_layouts.size()) if (layout_index >= m_layouts.size())
return; return;
DockLayout& layout = m_layouts[layout_index]; DockLayout& layout = m_layouts[layout_index];
QMenu* menu = new QMenu(m_switcher); QMenu* menu = new QMenu(layout_switcher);
menu->setAttribute(Qt::WA_DeleteOnClose); menu->setAttribute(Qt::WA_DeleteOnClose);
QAction* edit_action = menu->addAction(tr("Edit Layout")); QAction* edit_action = menu->addAction(tr("Edit Layout"));
connect(edit_action, &QAction::triggered, [this, layout_index]() { connect(edit_action, &QAction::triggered, [this, layout_index]() {
editLayoutClicked(layout_index);
});
QAction* reset_action = menu->addAction(tr("Reset Layout"));
reset_action->setEnabled(layout.canReset());
reset_action->connect(reset_action, &QAction::triggered, [this, layout_index]() {
resetLayoutClicked(layout_index);
});
QAction* delete_action = menu->addAction(tr("Delete Layout"));
connect(delete_action, &QAction::triggered, [this, layout_index]() {
deleteLayoutClicked(layout_index);
});
menu->popup(layout_switcher->mapToGlobal(pos));
}
void DockManager::editLayoutClicked(DockLayout::Index layout_index)
{
if (layout_index >= m_layouts.size()) if (layout_index >= m_layouts.size())
return; return;
@ -696,11 +613,10 @@ void DockManager::layoutSwitcherContextMenu(QPoint pos)
delete dialog.get(); delete dialog.get();
updateLayoutSwitcher(); updateLayoutSwitcher();
}); }
QAction* reset_action = menu->addAction(tr("Reset Layout")); void DockManager::resetLayoutClicked(DockLayout::Index layout_index)
reset_action->setEnabled(layout.canReset()); {
reset_action->connect(reset_action, &QAction::triggered, [this, layout_index]() {
if (layout_index >= m_layouts.size()) if (layout_index >= m_layouts.size())
return; return;
@ -722,10 +638,10 @@ void DockManager::layoutSwitcherContextMenu(QPoint pos)
if (current_layout) if (current_layout)
switchToLayout(layout_index); switchToLayout(layout_index);
}); }
QAction* delete_action = menu->addAction(tr("Delete Layout")); void DockManager::deleteLayoutClicked(DockLayout::Index layout_index)
connect(delete_action, &QAction::triggered, [this, layout_index]() { {
if (layout_index >= m_layouts.size()) if (layout_index >= m_layouts.size())
return; return;
@ -737,56 +653,30 @@ void DockManager::layoutSwitcherContextMenu(QPoint pos)
deleteLayout(layout_index); deleteLayout(layout_index);
updateLayoutSwitcher(); updateLayoutSwitcher();
});
menu->popup(m_switcher->mapToGlobal(pos));
} }
void DockManager::layoutSwitcherStartBlink() void DockManager::layoutSwitcherTabMoved(DockLayout::Index from_index, DockLayout::Index to_index)
{ {
if (!m_switcher) if (from_index >= m_layouts.size() || to_index >= m_layouts.size())
return;
layoutSwitcherStopBlink();
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_blink_tab = m_current_layout;
m_blink_stage = 0;
m_blink_timer->start(500);
layoutSwitcherUpdateBlink();
}
void DockManager::layoutSwitcherUpdateBlink()
{
if (!m_switcher)
return;
if (m_blink_tab < m_switcher->count())
{ {
if (m_blink_stage % 2 == 0) // This happens when the user tries to move a layout to the right of the
m_switcher->setTabTextColor(m_blink_tab, Qt::red); // plus button.
else updateLayoutSwitcher();
m_switcher->setTabTextColor(m_blink_tab, m_switcher->palette().text().color()); return;
} }
m_blink_stage++; DockLayout& from_layout = m_layouts[from_index];
DockLayout& to_layout = m_layouts[to_index];
if (m_blink_stage > 7) std::swap(from_layout, to_layout);
m_blink_timer->stop();
}
void DockManager::layoutSwitcherStopBlink() from_layout.save(from_index);
{ to_layout.save(to_index);
if (m_blink_timer->isActive())
{
if (m_blink_tab < m_switcher->count())
m_switcher->setTabTextColor(m_blink_tab, m_switcher->palette().text().color());
m_blink_timer->stop(); if (from_index == m_current_layout)
} m_current_layout = to_index;
else if (to_index == m_current_layout)
m_current_layout = from_index;
} }
bool DockManager::hasNameConflict(const QString& name, DockLayout::Index layout_index) bool DockManager::hasNameConflict(const QString& name, DockLayout::Index layout_index)
@ -879,23 +769,17 @@ bool DockManager::isLayoutLocked()
return m_layout_locked; return m_layout_locked;
} }
void DockManager::setLayoutLocked(bool locked, QPushButton* lock_layout_toggle, bool write_back) void DockManager::setLayoutLockedAndSaveSetting(bool locked)
{
setLayoutLocked(locked, true);
}
void DockManager::setLayoutLocked(bool locked, bool save_setting)
{ {
m_layout_locked = locked; m_layout_locked = locked;
if (lock_layout_toggle) if (m_menu_bar)
{ m_menu_bar->onLockStateChanged(locked);
if (m_layout_locked)
{
lock_layout_toggle->setText(tr("Layout Locked"));
lock_layout_toggle->setIcon(QIcon::fromTheme(QString::fromUtf8("padlock-lock")));
}
else
{
lock_layout_toggle->setText(tr("Layout Unlocked"));
lock_layout_toggle->setIcon(QIcon::fromTheme(QString::fromUtf8("padlock-unlock")));
}
}
updateToolBarLockState(); updateToolBarLockState();
@ -909,7 +793,7 @@ void DockManager::setLayoutLocked(bool locked, QPushButton* lock_layout_toggle,
stack->tabBar()->setTabText(0, stack->tabBar()->tabText(0)); stack->tabBar()->setTabText(0, stack->tabBar()->tabText(0));
} }
if (write_back) if (save_setting)
{ {
Host::SetBaseBoolSettingValue("Debugger/UserInterface", "LayoutLocked", m_layout_locked); Host::SetBaseBoolSettingValue("Debugger/UserInterface", "LayoutLocked", m_layout_locked);
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "Debugger/Docking/DockLayout.h" #include "Debugger/Docking/DockLayout.h"
#include "Debugger/Docking/DockMenuBar.h"
#include <kddockwidgets/MainWindow.h> #include <kddockwidgets/MainWindow.h>
#include <kddockwidgets/DockWidget.h> #include <kddockwidgets/DockWidget.h>
@ -68,14 +69,14 @@ public:
void createToolsMenu(QMenu* menu); void createToolsMenu(QMenu* menu);
void createWindowsMenu(QMenu* menu); void createWindowsMenu(QMenu* menu);
QWidget* createLayoutSwitcher(QWidget* menu_bar); QWidget* createMenuBar(QWidget* original_menu_bar);
void updateLayoutSwitcher(); void updateLayoutSwitcher();
void layoutSwitcherTabChanged(int index); void newLayoutClicked();
void layoutSwitcherTabMoved(int from, int to); void openLayoutSwitcherContextMenu(const QPoint& pos, QTabBar* layout_switcher);
void layoutSwitcherContextMenu(QPoint pos); void editLayoutClicked(DockLayout::Index layout_index);
void layoutSwitcherStartBlink(); void resetLayoutClicked(DockLayout::Index layout_index);
void layoutSwitcherUpdateBlink(); void deleteLayoutClicked(DockLayout::Index layout_index);
void layoutSwitcherStopBlink(); void layoutSwitcherTabMoved(DockLayout::Index from_index, DockLayout::Index to_index);
bool hasNameConflict(const QString& name, DockLayout::Index layout_index); bool hasNameConflict(const QString& name, DockLayout::Index layout_index);
@ -91,7 +92,8 @@ public:
void updateStyleSheets(); void updateStyleSheets();
bool isLayoutLocked(); bool isLayoutLocked();
void setLayoutLocked(bool locked, QPushButton* lock_layout_toggle, bool write_back); void setLayoutLockedAndSaveSetting(bool locked);
void setLayoutLocked(bool locked, bool save_setting);
void updateToolBarLockState(); void updateToolBarLockState();
std::optional<BreakPointCpu> cpu(); std::optional<BreakPointCpu> cpu();
@ -103,16 +105,7 @@ private:
std::vector<DockLayout> m_layouts; std::vector<DockLayout> m_layouts;
DockLayout::Index m_current_layout = DockLayout::INVALID_INDEX; DockLayout::Index m_current_layout = DockLayout::INVALID_INDEX;
QTabBar* m_switcher = nullptr; DockMenuBar* m_menu_bar = nullptr;
int m_plus_tab_index = -1;
int m_current_tab_index = -1;
bool m_ignore_current_tab_changed = false;
QMetaObject::Connection m_tab_connection;
bool m_layout_locked = true; bool m_layout_locked = true;
QTimer* m_blink_timer = nullptr;
int m_blink_tab = 0;
int m_blink_stage = 0;
}; };

View File

@ -0,0 +1,184 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "DockMenuBar.h"
#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include <QtWidgets/QBoxLayout>
#include <QtWidgets/QStyleOption>
DockMenuBar::DockMenuBar(QWidget* original_menu_bar, QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins(0, 2, 2, 0);
setLayout(layout);
QWidget* menu_wrapper = new QWidget;
menu_wrapper->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
layout->addWidget(menu_wrapper);
QHBoxLayout* menu_layout = new QHBoxLayout;
menu_layout->setContentsMargins(0, 4, 0, 4);
menu_wrapper->setLayout(menu_layout);
menu_layout->addWidget(original_menu_bar);
m_layout_switcher = new QTabBar;
m_layout_switcher->setContentsMargins(0, 0, 0, 0);
m_layout_switcher->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
m_layout_switcher->setContextMenuPolicy(Qt::CustomContextMenu);
m_layout_switcher->setMovable(true);
layout->addWidget(m_layout_switcher);
QWidget* spacer = new QWidget;
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
layout->addWidget(spacer);
connect(m_layout_switcher, &QTabBar::tabMoved, this, [this](int from, int to) {
DockLayout::Index from_index = static_cast<DockLayout::Index>(from);
DockLayout::Index to_index = static_cast<DockLayout::Index>(to);
emit layoutMoved(from_index, to_index);
});
connect(m_layout_switcher, &QTabBar::customContextMenuRequested, this, [this](const QPoint& pos) {
emit layoutSwitcherContextMenuRequested(pos, m_layout_switcher);
});
m_blink_timer = new QTimer(this);
connect(m_blink_timer, &QTimer::timeout, this, &DockMenuBar::updateBlink);
m_layout_locked_toggle = new QPushButton;
m_layout_locked_toggle->setCheckable(true);
m_layout_locked_toggle->setFlat(true);
connect(m_layout_locked_toggle, &QPushButton::clicked, this, [this](bool checked) {
if (m_ignore_lock_state_changed)
return;
emit lockButtonToggled(checked);
});
layout->addWidget(m_layout_locked_toggle);
}
void DockMenuBar::updateLayoutSwitcher(DockLayout::Index current_index, const std::vector<DockLayout>& layouts)
{
disconnect(m_tab_connection);
for (int i = m_layout_switcher->count(); i > 0; i--)
m_layout_switcher->removeTab(i - 1);
for (const DockLayout& layout : layouts)
{
const char* cpu_name = DebugInterface::cpuName(layout.cpu());
QString tab_name = QString("%1 (%2)").arg(layout.name()).arg(cpu_name);
m_layout_switcher->addTab(tab_name);
}
m_plus_tab_index = m_layout_switcher->addTab("+");
m_current_tab_index = current_index;
if (current_index != DockLayout::INVALID_INDEX)
m_layout_switcher->setCurrentIndex(current_index);
else
m_layout_switcher->setCurrentIndex(m_plus_tab_index);
// If we don't have any layouts, the currently selected tab will never be
// changed, so we respond to all clicks instead.
if (m_plus_tab_index > 0)
m_tab_connection = connect(m_layout_switcher, &QTabBar::currentChanged, this, &DockMenuBar::tabChanged);
else
m_tab_connection = connect(m_layout_switcher, &QTabBar::tabBarClicked, this, &DockMenuBar::tabChanged);
stopBlink();
}
void DockMenuBar::onCurrentLayoutChanged(DockLayout::Index current_index)
{
m_ignore_current_tab_changed = true;
if (current_index != DockLayout::INVALID_INDEX)
m_layout_switcher->setCurrentIndex(current_index);
else
m_layout_switcher->setCurrentIndex(m_plus_tab_index);
m_ignore_current_tab_changed = false;
}
void DockMenuBar::onLockStateChanged(bool layout_locked)
{
m_ignore_lock_state_changed = true;
m_layout_locked_toggle->setChecked(layout_locked);
if (layout_locked)
{
m_layout_locked_toggle->setText(tr("Layout Locked"));
m_layout_locked_toggle->setIcon(QIcon::fromTheme(QString::fromUtf8("padlock-lock")));
}
else
{
m_layout_locked_toggle->setText(tr("Layout Unlocked"));
m_layout_locked_toggle->setIcon(QIcon::fromTheme(QString::fromUtf8("padlock-unlock")));
}
m_ignore_lock_state_changed = false;
}
void DockMenuBar::startBlink(DockLayout::Index layout_index)
{
stopBlink();
if (layout_index == DockLayout::INVALID_INDEX)
return;
m_blink_tab = static_cast<int>(layout_index);
m_blink_stage = 0;
m_blink_timer->start(500);
updateBlink();
}
void DockMenuBar::updateBlink()
{
if (m_blink_tab < m_layout_switcher->count())
{
if (m_blink_stage % 2 == 0)
m_layout_switcher->setTabTextColor(m_blink_tab, Qt::red);
else
m_layout_switcher->setTabTextColor(m_blink_tab, m_layout_switcher->palette().text().color());
}
m_blink_stage++;
if (m_blink_stage > 7)
m_blink_timer->stop();
}
void DockMenuBar::stopBlink()
{
if (m_blink_timer->isActive())
{
if (m_blink_tab < m_layout_switcher->count())
m_layout_switcher->setTabTextColor(m_blink_tab, m_layout_switcher->palette().text().color());
m_blink_timer->stop();
}
}
void DockMenuBar::tabChanged(int index)
{
// Prevent recursion.
if (m_ignore_current_tab_changed)
return;
if (index < m_plus_tab_index)
{
DockLayout::Index layout_index = static_cast<DockLayout::Index>(index);
emit currentLayoutChanged(layout_index);
}
else if (index == m_plus_tab_index)
{
emit newButtonClicked();
}
}

View File

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "Debugger/Docking/DockLayout.h"
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTabBar>
#include <QtWidgets/QWidget>
class DockMenuBar : public QWidget
{
Q_OBJECT
public:
DockMenuBar(QWidget* original_menu_bar, QWidget* parent = nullptr);
void updateLayoutSwitcher(DockLayout::Index current_index, const std::vector<DockLayout>& layouts);
// Notify the menu bar that a new layout has been selected.
void onCurrentLayoutChanged(DockLayout::Index current_index);
// Notify the menu bar that the layout has been locked/unlocked.
void onLockStateChanged(bool layout_locked);
void startBlink(DockLayout::Index layout_index);
void updateBlink();
void stopBlink();
Q_SIGNALS:
void currentLayoutChanged(DockLayout::Index layout_index);
void newButtonClicked();
void layoutMoved(DockLayout::Index from_index, DockLayout::Index to_index);
void lockButtonToggled(bool locked);
void layoutSwitcherContextMenuRequested(const QPoint& pos, QTabBar* layout_switcher);
private:
void tabChanged(int index);
QTabBar* m_layout_switcher;
QMetaObject::Connection m_tab_connection;
int m_plus_tab_index = -1;
int m_current_tab_index = -1;
bool m_ignore_current_tab_changed = false;
QTimer* m_blink_timer = nullptr;
int m_blink_tab = 0;
int m_blink_stage = 0;
QPushButton* m_layout_locked_toggle;
bool m_ignore_lock_state_changed = false;
};

View File

@ -126,6 +126,7 @@
<ClCompile Include="Debugger\Breakpoints\BreakpointWidget.cpp" /> <ClCompile Include="Debugger\Breakpoints\BreakpointWidget.cpp" />
<ClCompile Include="Debugger\Docking\DockLayout.cpp" /> <ClCompile Include="Debugger\Docking\DockLayout.cpp" />
<ClCompile Include="Debugger\Docking\DockManager.cpp" /> <ClCompile Include="Debugger\Docking\DockManager.cpp" />
<ClCompile Include="Debugger\Docking\DockMenuBar.cpp" />
<ClCompile Include="Debugger\Docking\DockTables.cpp" /> <ClCompile Include="Debugger\Docking\DockTables.cpp" />
<ClCompile Include="Debugger\Docking\DockUtils.cpp" /> <ClCompile Include="Debugger\Docking\DockUtils.cpp" />
<ClCompile Include="Debugger\Docking\DockViews.cpp" /> <ClCompile Include="Debugger\Docking\DockViews.cpp" />
@ -242,6 +243,7 @@
<QtMoc Include="Debugger\Breakpoints\BreakpointWidget.h" /> <QtMoc Include="Debugger\Breakpoints\BreakpointWidget.h" />
<QtMoc Include="Debugger\Docking\DockLayout.h" /> <QtMoc Include="Debugger\Docking\DockLayout.h" />
<QtMoc Include="Debugger\Docking\DockManager.h" /> <QtMoc Include="Debugger\Docking\DockManager.h" />
<QtMoc Include="Debugger\Docking\DockMenuBar.h" />
<QtMoc Include="Debugger\Docking\DockTables.h" /> <QtMoc Include="Debugger\Docking\DockTables.h" />
<QtMoc Include="Debugger\Docking\DockUtils.h" /> <QtMoc Include="Debugger\Docking\DockUtils.h" />
<QtMoc Include="Debugger\Docking\DockViews.h" /> <QtMoc Include="Debugger\Docking\DockViews.h" />
@ -313,6 +315,7 @@
<ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointModel.cpp" /> <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointModel.cpp" />
<ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointWidget.cpp" /> <ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointWidget.cpp" />
<ClCompile Include="$(IntDir)Debugger\Docking\moc_DockManager.cpp" /> <ClCompile Include="$(IntDir)Debugger\Docking\moc_DockManager.cpp" />
<ClCompile Include="$(IntDir)Debugger\Docking\moc_DockMenuBar.cpp" />
<ClCompile Include="$(IntDir)Debugger\Docking\moc_DockViews.cpp" /> <ClCompile Include="$(IntDir)Debugger\Docking\moc_DockViews.cpp" />
<ClCompile Include="$(IntDir)Debugger\Docking\moc_DropIndicators.cpp" /> <ClCompile Include="$(IntDir)Debugger\Docking\moc_DropIndicators.cpp" />
<ClCompile Include="$(IntDir)Debugger\Docking\moc_LayoutEditorDialog.cpp" /> <ClCompile Include="$(IntDir)Debugger\Docking\moc_LayoutEditorDialog.cpp" />

View File

@ -488,6 +488,12 @@
<ClCompile Include="$(IntDir)Debugger\SymbolTree\moc_TypeString.cpp"> <ClCompile Include="$(IntDir)Debugger\SymbolTree\moc_TypeString.cpp">
<Filter>moc</Filter> <Filter>moc</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="$(IntDir)Debugger\Docking\moc_DockMenuBar.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="Debugger\Docking\DockMenuBar.cpp">
<Filter>Debugger\Docking</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest"> <Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -719,6 +725,9 @@
<QtMoc Include="Debugger\SymbolTree\NewSymbolDialogs.h"> <QtMoc Include="Debugger\SymbolTree\NewSymbolDialogs.h">
<Filter>Debugger\SymbolTree</Filter> <Filter>Debugger\SymbolTree</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="Debugger\Docking\DockMenuBar.h">
<Filter>Debugger\Docking</Filter>
</QtMoc>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtResource Include="resources\resources.qrc"> <QtResource Include="resources\resources.qrc">