diff --git a/pcsx2-qt/Debugger/DebuggerWindow.cpp b/pcsx2-qt/Debugger/DebuggerWindow.cpp index 7ceb192ac5..8a8e587ead 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.cpp +++ b/pcsx2-qt/Debugger/DebuggerWindow.cpp @@ -25,6 +25,8 @@ DebuggerWindow::DebuggerWindow(QWidget* parent) g_debugger_window = this; + setupDefaultToolBarState(); + m_dock_manager->loadLayouts(); connect(m_ui.actionRun, &QAction::triggered, this, &DebuggerWindow::onRunPause); @@ -34,6 +36,10 @@ DebuggerWindow::DebuggerWindow(QWidget* parent) connect(m_ui.actionAnalyse, &QAction::triggered, this, &DebuggerWindow::onAnalyse); connect(m_ui.actionOnTop, &QAction::triggered, [this] { this->setWindowFlags(this->windowFlags() ^ Qt::WindowStaysOnTopHint); this->show(); }); + connect(m_ui.menuTools, &QMenu::aboutToShow, this, [this]() { + m_dock_manager->createToolsMenu(m_ui.menuTools); + }); + connect(m_ui.menuWindows, &QMenu::aboutToShow, this, [this]() { m_dock_manager->createWindowsMenu(m_ui.menuWindows); }); @@ -100,6 +106,11 @@ DockManager& DebuggerWindow::dockManager() return *m_dock_manager; } +void DebuggerWindow::clearToolBarState() +{ + restoreState(m_default_toolbar_state); +} + // There is no straightforward way to set the tab text to bold in Qt // Sorry colour blind people, but this is the best we can do for now void DebuggerWindow::setTabActiveStyle(BreakPointCpu enabledCpu) @@ -198,3 +209,18 @@ void DebuggerWindow::closeEvent(QCloseEvent* event) g_debugger_window = nullptr; deleteLater(); } + +void DebuggerWindow::setupDefaultToolBarState() +{ + // Hiding all the toolbars lets us save the default state of the window with + // all the toolbars hidden. The DockManager will show the appropriate ones + // later anyway. + for (QToolBar* toolbar : findChildren()) + toolbar->hide(); + + m_default_toolbar_state = saveState(); + + + for (QToolBar* toolbar : findChildren()) + connect(toolbar, &QToolBar::topLevelChanged, m_dock_manager, &DockManager::updateToolBarLockState); +} diff --git a/pcsx2-qt/Debugger/DebuggerWindow.h b/pcsx2-qt/Debugger/DebuggerWindow.h index bcfa935aec..b90b629076 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.h +++ b/pcsx2-qt/Debugger/DebuggerWindow.h @@ -24,6 +24,8 @@ public: DockManager& dockManager(); + void clearToolBarState(); + public slots: void onVMStateChanged(); void onRunPause(); @@ -36,6 +38,8 @@ protected: void closeEvent(QCloseEvent* event); private: + void setupDefaultToolBarState(); + Ui::DebuggerWindow m_ui; QAction* m_actionRunPause; QAction* m_actionStepInto; @@ -44,6 +48,8 @@ private: DockManager* m_dock_manager; + QByteArray m_default_toolbar_state; + void setTabActiveStyle(BreakPointCpu toggledCPU); }; diff --git a/pcsx2-qt/Debugger/DebuggerWindow.ui b/pcsx2-qt/Debugger/DebuggerWindow.ui index fac077dc57..0f76916069 100644 --- a/pcsx2-qt/Debugger/DebuggerWindow.ui +++ b/pcsx2-qt/Debugger/DebuggerWindow.ui @@ -18,10 +18,13 @@ :/icons/AppIcon64.png - + Qt::PreventContextMenu + + Debug + true @@ -90,15 +93,21 @@ + + + Tools + + + - + - toolBar + View TopToolBarArea diff --git a/pcsx2-qt/Debugger/Docking/DockLayout.cpp b/pcsx2-qt/Debugger/Docking/DockLayout.cpp index 2bf8471528..7093cd3d9d 100644 --- a/pcsx2-qt/Debugger/Docking/DockLayout.cpp +++ b/pcsx2-qt/Debugger/Docking/DockLayout.cpp @@ -49,12 +49,12 @@ DockLayout::DockLayout( for (size_t i = 0; i < default_layout.widgets.size(); i++) { - auto iterator = DockTables::DEBUGGER_WIDGETS.find(default_layout.widgets[i].type); + auto iterator = DockTables::DEBUGGER_WIDGETS.find(QString::fromStdString(default_layout.widgets[i].type)); pxAssertRel(iterator != DockTables::DEBUGGER_WIDGETS.end(), "Invalid default layout."); const DockTables::DebuggerWidgetDescription& dock_description = iterator->second; DebuggerWidget* widget = dock_description.create_widget(debug_interface); - m_widgets.emplace(default_layout.widgets[i].type, widget); + m_widgets.emplace(QString::fromStdString(default_layout.widgets[i].type), widget); } save(index); @@ -82,6 +82,7 @@ DockLayout::DockLayout( , m_cpu(cpu) , m_is_default(is_default) , m_base_layout(layout_to_clone.m_base_layout) + , m_toolbars(layout_to_clone.m_toolbars) , m_geometry(layout_to_clone.m_geometry) { for (const auto& [unique_name, widget_to_clone] : layout_to_clone.m_widgets) @@ -154,6 +155,8 @@ void DockLayout::freeze() pxAssert(!m_is_frozen); m_is_frozen = true; + m_toolbars = g_debugger_window->saveState(); + // Store the geometry of all the dock widgets as JSON. KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow); m_geometry = saver.serializeLayout(); @@ -169,21 +172,36 @@ void DockLayout::freeze() } } -void DockLayout::thaw(DebuggerWindow* window) +void DockLayout::thaw() { pxAssert(m_is_frozen); m_is_frozen = false; - KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow); + // Restore the state of the toolbars. + if (m_toolbars.isEmpty()) + { + const DockTables::DefaultDockLayout* base_layout = DockTables::defaultLayout(m_base_layout); + if (base_layout) + { + for (QToolBar* toolbar : g_debugger_window->findChildren()) + if (base_layout->toolbars.contains(toolbar->objectName().toStdString())) + toolbar->show(); + } + } + else + { + g_debugger_window->restoreState(m_toolbars); + } if (m_geometry.isEmpty()) { // This is a newly created layout with no geometry information. - setupDefaultLayout(window); + setupDefaultLayout(); return; } // Restore the geometry of the dock widgets we just recreated. + KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow); if (!saver.restoreLayout(m_geometry)) { // We've failed to restore the geometry, so just tear down whatever dock @@ -197,7 +215,7 @@ void DockLayout::thaw(DebuggerWindow* window) delete dock; } - setupDefaultLayout(window); + setupDefaultLayout(); return; } @@ -301,7 +319,7 @@ bool DockLayout::hasDebuggerWidget(QString unique_name) return m_widgets.find(unique_name) != m_widgets.end(); } -void DockLayout::toggleDebuggerWidget(QString unique_name, DebuggerWindow* window) +void DockLayout::toggleDebuggerWidget(QString unique_name) { pxAssert(!m_is_frozen); @@ -334,7 +352,7 @@ void DockLayout::toggleDebuggerWidget(QString unique_name, DebuggerWindow* windo return; } - DockUtils::insertDockWidgetAtPreferredLocation(controller, description.preferred_location, window); + DockUtils::insertDockWidgetAtPreferredLocation(controller, description.preferred_location, g_debugger_window); retranslateDockWidget(controller); } else @@ -386,11 +404,12 @@ void DockLayout::deleteFile() if (!FileSystem::DeleteFilePath(m_layout_file_path.c_str())) Console.Error("Debugger: Failed to delete layout file '%s'.", m_layout_file_path.c_str()); } - bool DockLayout::save(DockLayout::Index layout_index) { if (!m_is_frozen) { + m_toolbars = g_debugger_window->saveState(); + // Store the geometry of all the dock widgets as JSON. KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow); m_geometry = saver.serializeLayout(); @@ -425,6 +444,14 @@ bool DockLayout::save(DockLayout::Index layout_index) json.AddMember("baseLayout", base_layout, json.GetAllocator()); } + if (!m_toolbars.isEmpty()) + { + std::string toolbars_str = m_toolbars.toBase64().toStdString(); + rapidjson::Value toolbars; + toolbars.SetString(toolbars_str.data(), toolbars_str.size(), json.GetAllocator()); + json.AddMember("toolbars", toolbars, json.GetAllocator()); + } + rapidjson::Value widgets(rapidjson::kArrayType); for (auto& [unique_name, widget] : m_widgets) { @@ -584,6 +611,10 @@ void DockLayout::load( if (base_layout != json.MemberEnd() && base_layout->value.IsString()) m_base_layout = base_layout->value.GetString(); + auto toolbars = json.FindMember("toolbars"); + if (toolbars != json.MemberEnd() && toolbars->value.IsString()) + m_toolbars = QByteArray::fromBase64(toolbars->value.GetString()); + auto widgets = json.FindMember("widgets"); if (widgets != json.MemberEnd() && widgets->value.IsArray()) { @@ -627,28 +658,25 @@ void DockLayout::load( m_layout_file_path = path; } -void DockLayout::setupDefaultLayout(DebuggerWindow* window) +void DockLayout::setupDefaultLayout() { pxAssert(!m_is_frozen); if (m_base_layout.empty()) return; - const DockTables::DefaultDockLayout* layout = nullptr; - for (const DockTables::DefaultDockLayout& default_layout : DockTables::DEFAULT_DOCK_LAYOUTS) - if (default_layout.name == m_base_layout) - layout = &default_layout; - - if (!layout) + const DockTables::DefaultDockLayout* base_layout = DockTables::defaultLayout(m_base_layout); + if (!base_layout) return; - std::vector groups(layout->groups.size(), nullptr); + std::vector groups(base_layout->groups.size(), nullptr); - for (const DockTables::DefaultDockWidgetDescription& dock_description : layout->widgets) + for (const DockTables::DefaultDockWidgetDescription& dock_description : base_layout->widgets) { - const DockTables::DefaultDockGroupDescription& group = layout->groups[static_cast(dock_description.group)]; + const DockTables::DefaultDockGroupDescription& group = + base_layout->groups[static_cast(dock_description.group)]; - auto widget_iterator = m_widgets.find(dock_description.type); + auto widget_iterator = m_widgets.find(QString::fromStdString(dock_description.type)); if (widget_iterator == m_widgets.end()) continue; @@ -665,7 +693,7 @@ void DockLayout::setupDefaultLayout(DebuggerWindow* window) if (group.parent != DockTables::DefaultDockGroup::ROOT) parent = groups[static_cast(group.parent)]; - window->addDockWidget(view, group.location, parent); + g_debugger_window->addDockWidget(view, group.location, parent); groups[static_cast(dock_description.group)] = view; } diff --git a/pcsx2-qt/Debugger/Docking/DockLayout.h b/pcsx2-qt/Debugger/Docking/DockLayout.h index 0c58bdc7a9..6c7c16f649 100644 --- a/pcsx2-qt/Debugger/Docking/DockLayout.h +++ b/pcsx2-qt/Debugger/Docking/DockLayout.h @@ -89,7 +89,7 @@ public: void freeze(); // Restore the state of all the dock widgets from this layout. - void thaw(DebuggerWindow* window); + void thaw(); KDDockWidgets::Core::DockWidget* createDockWidget(const QString& name); void retranslateDockWidgets(); @@ -97,7 +97,7 @@ public: void dockWidgetClosed(KDDockWidgets::Core::DockWidget* dock_widget); bool hasDebuggerWidget(QString unique_name); - void toggleDebuggerWidget(QString unique_name, DebuggerWindow* window); + void toggleDebuggerWidget(QString unique_name); void recreateDebuggerWidget(QString unique_name); void deleteFile(); @@ -110,7 +110,7 @@ private: DockLayout::LoadResult& result, DockLayout::Index& index_last_session); - void setupDefaultLayout(DebuggerWindow* window); + void setupDefaultLayout(); // The name displayed in the user interface. Also used to determine the // file name for the layout file. @@ -127,6 +127,10 @@ private: // be used if the m_geometry variable above is empty. std::string m_base_layout; + // The state of all the toolbars, saved and restored using + // QMainWindow::saveState and QMainWindow::restoreState respectively. + QByteArray m_toolbars; + // All the dock widgets currently open in this layout. If this is the active // layout then these will be owned by the docking system, otherwise they // won't be and will need to be cleaned up separately. diff --git a/pcsx2-qt/Debugger/Docking/DockManager.cpp b/pcsx2-qt/Debugger/Docking/DockManager.cpp index 86dd58603d..96c363a7fe 100644 --- a/pcsx2-qt/Debugger/Docking/DockManager.cpp +++ b/pcsx2-qt/Debugger/Docking/DockManager.cpp @@ -104,12 +104,17 @@ void DockManager::switchToLayout(DockLayout::Index layout_index) layout.save(m_current_layout); } + // Clear out the existing positions of toolbars so they don't affect where + // new toolbars appear for other layouts. + g_debugger_window->clearToolBarState(); + updateToolBarLockState(); + m_current_layout = layout_index; if (m_current_layout != DockLayout::INVALID_INDEX) { DockLayout& layout = m_layouts.at(m_current_layout); - layout.thaw(g_debugger_window); + layout.thaw(); } } @@ -265,6 +270,26 @@ void DockManager::resetDefaultLayouts() saveLayouts(); } +void DockManager::createToolsMenu(QMenu* menu) +{ + menu->clear(); + + if (m_current_layout == DockLayout::INVALID_INDEX) + return; + + for (QToolBar* widget : g_debugger_window->findChildren()) + { + QAction* action = new QAction(menu); + action->setText(widget->windowTitle()); + action->setCheckable(true); + action->setChecked(widget->isVisible()); + connect(action, &QAction::triggered, this, [widget]() { + widget->setVisible(!widget->isVisible()); + }); + menu->addAction(action); + } +} + void DockManager::createWindowsMenu(QMenu* menu) { menu->clear(); @@ -281,7 +306,7 @@ void DockManager::createWindowsMenu(QMenu* menu) action->setCheckable(true); action->setChecked(layout.hasDebuggerWidget(type)); connect(action, &QAction::triggered, this, [&layout, type]() { - layout.toggleDebuggerWidget(type, g_debugger_window); + layout.toggleDebuggerWidget(type); }); menu->addAction(action); } @@ -557,6 +582,8 @@ void DockManager::setLayoutLocked(bool locked) { m_layout_locked = locked; + updateToolBarLockState(); + for (KDDockWidgets::Core::Group* group : KDDockWidgets::DockRegistry::self()->groups()) { auto stack = static_cast(group->stack()->view()); @@ -568,6 +595,12 @@ void DockManager::setLayoutLocked(bool locked) } } +void DockManager::updateToolBarLockState() +{ + for (QToolBar* toolbar : g_debugger_window->findChildren()) + toolbar->setMovable(!m_layout_locked || toolbar->isFloating()); +} + KDDockWidgets::Core::DockWidget* DockManager::dockWidgetFactory(const QString& name) { if (!g_debugger_window) diff --git a/pcsx2-qt/Debugger/Docking/DockManager.h b/pcsx2-qt/Debugger/Docking/DockManager.h index d9b4a10711..fb921625e5 100644 --- a/pcsx2-qt/Debugger/Docking/DockManager.h +++ b/pcsx2-qt/Debugger/Docking/DockManager.h @@ -59,6 +59,7 @@ public: void resetAllLayouts(); void resetDefaultLayouts(); + void createToolsMenu(QMenu* menu); void createWindowsMenu(QMenu* menu); QWidget* createLayoutSwitcher(QWidget* menu_bar); @@ -76,6 +77,7 @@ public: bool isLayoutLocked(); void setLayoutLocked(bool locked); + void updateToolBarLockState(); private: static KDDockWidgets::Core::DockWidget* dockWidgetFactory(const QString& name); diff --git a/pcsx2-qt/Debugger/Docking/DockTables.cpp b/pcsx2-qt/Debugger/Docking/DockTables.cpp index 5d625b3c21..aff8e5f3f8 100644 --- a/pcsx2-qt/Debugger/Docking/DockTables.cpp +++ b/pcsx2-qt/Debugger/Docking/DockTables.cpp @@ -48,14 +48,14 @@ const std::map DockTables::DEBUG const std::vector DockTables::DEFAULT_DOCK_LAYOUTS = { { - QT_TRANSLATE_NOOP("DebuggerLayout", "R5900"), - BREAKPOINT_EE, - { + .name = QT_TRANSLATE_NOOP("DebuggerLayout", "R5900"), + .cpu = BREAKPOINT_EE, + .groups = { /* [DefaultDockGroup::TOP_RIGHT] = */ {KDDockWidgets::Location_OnRight, DefaultDockGroup::ROOT}, /* [DefaultDockGroup::BOTTOM] = */ {KDDockWidgets::Location_OnBottom, DefaultDockGroup::TOP_RIGHT}, /* [DefaultDockGroup::TOP_LEFT] = */ {KDDockWidgets::Location_OnLeft, DefaultDockGroup::TOP_RIGHT}, }, - { + .widgets = { /* DefaultDockGroup::TOP_RIGHT */ {"DisassemblyWidget", DefaultDockGroup::TOP_RIGHT}, /* DefaultDockGroup::BOTTOM */ @@ -72,16 +72,19 @@ const std::vector DockTables::DEFAULT_DOCK_LAYOUT {"FunctionTreeWidget", DefaultDockGroup::TOP_LEFT}, {"MemorySearchWidget", DefaultDockGroup::TOP_LEFT}, }, + .toolbars = { + "toolBarDebug", + }, }, { - QT_TRANSLATE_NOOP("DebuggerLayout", "R3000"), - BREAKPOINT_IOP, - { + .name = QT_TRANSLATE_NOOP("DebuggerLayout", "R3000"), + .cpu = BREAKPOINT_IOP, + .groups = { /* [DefaultDockGroup::TOP_RIGHT] = */ {KDDockWidgets::Location_OnRight, DefaultDockGroup::ROOT}, /* [DefaultDockGroup::BOTTOM] = */ {KDDockWidgets::Location_OnBottom, DefaultDockGroup::TOP_RIGHT}, /* [DefaultDockGroup::TOP_LEFT] = */ {KDDockWidgets::Location_OnLeft, DefaultDockGroup::TOP_RIGHT}, }, - { + .widgets = { /* DefaultDockGroup::TOP_RIGHT */ {"DisassemblyWidget", DefaultDockGroup::TOP_RIGHT}, /* DefaultDockGroup::BOTTOM */ @@ -98,9 +101,21 @@ const std::vector DockTables::DEFAULT_DOCK_LAYOUT {"FunctionTreeWidget", DefaultDockGroup::TOP_LEFT}, {"MemorySearchWidget", DefaultDockGroup::TOP_LEFT}, }, + .toolbars = { + "toolBarDebug", + }, }, }; +const DockTables::DefaultDockLayout* DockTables::defaultLayout(const std::string& name) +{ + for (const DockTables::DefaultDockLayout& default_layout : DockTables::DEFAULT_DOCK_LAYOUTS) + if (default_layout.name == name) + return &default_layout; + + return nullptr; +} + const std::string& DockTables::hashDefaultLayouts() { static std::string hash; @@ -112,7 +127,7 @@ const std::string& DockTables::hashDefaultLayouts() u32 hash_version = 1; md5.Update(&hash_version, sizeof(hash_version)); - size_t layout_count = DEFAULT_DOCK_LAYOUTS.size(); + u32 layout_count = static_cast(DEFAULT_DOCK_LAYOUTS.size()); md5.Update(&layout_count, sizeof(layout_count)); for (const DefaultDockLayout& layout : DEFAULT_DOCK_LAYOUTS) @@ -130,32 +145,41 @@ const std::string& DockTables::hashDefaultLayouts() void DockTables::hashDefaultLayout(const DefaultDockLayout& layout, MD5Digest& md5) { - size_t layout_name_size = layout.name.size(); + u32 layout_name_size = static_cast(layout.name.size()); md5.Update(&layout_name_size, sizeof(layout_name_size)); - md5.Update(layout.name.c_str(), layout.name.size()); + md5.Update(layout.name.data(), layout_name_size); const char* cpu_name = DebugInterface::cpuName(layout.cpu); - size_t cpu_name_size = strlen(cpu_name); + u32 cpu_name_size = static_cast(strlen(cpu_name)); md5.Update(&cpu_name_size, sizeof(cpu_name_size)); md5.Update(cpu_name, cpu_name_size); - size_t group_count = layout.groups.size(); + u32 group_count = static_cast(layout.groups.size()); md5.Update(&group_count, sizeof(group_count)); for (const DefaultDockGroupDescription& group : layout.groups) hashDefaultGroup(group, md5); - size_t widget_count = layout.widgets.size(); + u32 widget_count = static_cast(layout.widgets.size()); md5.Update(&widget_count, sizeof(widget_count)); for (const DefaultDockWidgetDescription& widget : layout.widgets) hashDefaultDockWidget(widget, md5); + + u32 toolbar_count = static_cast(layout.toolbars.size()); + md5.Update(&toolbar_count, sizeof(toolbar_count)); + for (const std::string& toolbar : layout.toolbars) + { + u32 toolbar_size = toolbar.size(); + md5.Update(&toolbar_size, sizeof(toolbar_size)); + md5.Update(toolbar.data(), toolbar.size()); + } } void DockTables::hashDefaultGroup(const DefaultDockGroupDescription& group, MD5Digest& md5) { const char* location = DockUtils::locationToString(group.location); - size_t location_size = strlen(location); + u32 location_size = static_cast(strlen(location)); md5.Update(&location_size, sizeof(location_size)); md5.Update(location, location_size); @@ -165,10 +189,9 @@ void DockTables::hashDefaultGroup(const DefaultDockGroupDescription& group, MD5D void DockTables::hashDefaultDockWidget(const DefaultDockWidgetDescription& widget, MD5Digest& md5) { - std::string type = widget.type.toStdString(); - size_t type_size = type.size(); + u32 type_size = static_cast(widget.type.size()); md5.Update(&type_size, sizeof(type_size)); - md5.Update(type.c_str(), type_size); + md5.Update(widget.type.data(), type_size); u32 group = static_cast(widget.group); md5.Update(&group, sizeof(group)); diff --git a/pcsx2-qt/Debugger/Docking/DockTables.h b/pcsx2-qt/Debugger/Docking/DockTables.h index 55a31bb33c..cae001d8ec 100644 --- a/pcsx2-qt/Debugger/Docking/DockTables.h +++ b/pcsx2-qt/Debugger/Docking/DockTables.h @@ -48,7 +48,7 @@ namespace DockTables struct DefaultDockWidgetDescription { - QString type; + std::string type; DefaultDockGroup group; }; @@ -58,10 +58,13 @@ namespace DockTables BreakPointCpu cpu; std::vector groups; std::vector widgets; + std::set toolbars; }; extern const std::vector DEFAULT_DOCK_LAYOUTS; + const DefaultDockLayout* defaultLayout(const std::string& name); + // This is used to determine if the user has updated and we need to recreate // the default layouts. const std::string& hashDefaultLayouts();