// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team // SPDX-License-Identifier: GPL-3.0+ #include "DockViews.h" #include "QtUtils.h" #include "Debugger/DebuggerView.h" #include "Debugger/DebuggerWindow.h" #include "Debugger/Docking/DockManager.h" #include "Debugger/Docking/DropIndicators.h" #include "DebugTools/DebugInterface.h" #include #include #include #include #include #include #include #include #include KDDockWidgets::Core::View* DockViewFactory::createDockWidget( const QString& unique_name, KDDockWidgets::DockWidgetOptions options, KDDockWidgets::LayoutSaverOptions layout_saver_options, Qt::WindowFlags window_flags) const { return new DockWidget(unique_name, options, layout_saver_options, window_flags); } KDDockWidgets::Core::View* DockViewFactory::createTitleBar( KDDockWidgets::Core::TitleBar* controller, KDDockWidgets::Core::View* parent) const { return new DockTitleBar(controller, parent); } KDDockWidgets::Core::View* DockViewFactory::createStack( KDDockWidgets::Core::Stack* controller, KDDockWidgets::Core::View* parent) const { return new DockStack(controller, KDDockWidgets::QtCommon::View_qt::asQWidget(parent)); } KDDockWidgets::Core::View* DockViewFactory::createTabBar( KDDockWidgets::Core::TabBar* tabBar, KDDockWidgets::Core::View* parent) const { return new DockTabBar(tabBar, KDDockWidgets::QtCommon::View_qt::asQWidget(parent)); } KDDockWidgets::Core::ClassicIndicatorWindowViewInterface* DockViewFactory::createClassicIndicatorWindow( KDDockWidgets::Core::ClassicDropIndicatorOverlay* classic_indicators, KDDockWidgets::Core::View* parent) const { return new DockDropIndicatorProxy(classic_indicators); } KDDockWidgets::Core::ClassicIndicatorWindowViewInterface* DockViewFactory::createFallbackClassicIndicatorWindow( KDDockWidgets::Core::ClassicDropIndicatorOverlay* classic_indicators, KDDockWidgets::Core::View* parent) const { return KDDockWidgets::QtWidgets::ViewFactory::createClassicIndicatorWindow(classic_indicators, parent); } KDDockWidgets::Core::View* DockViewFactory::createSegmentedDropIndicatorOverlayView( KDDockWidgets::Core::SegmentedDropIndicatorOverlay* controller, KDDockWidgets::Core::View* parent) const { return new DockSegmentedDropIndicatorOverlay(controller, KDDockWidgets::QtCommon::View_qt::asQWidget(parent)); } // ***************************************************************************** DockWidget::DockWidget( const QString& unique_name, KDDockWidgets::DockWidgetOptions options, KDDockWidgets::LayoutSaverOptions layout_saver_options, Qt::WindowFlags window_flags) : KDDockWidgets::QtWidgets::DockWidget(unique_name, options, layout_saver_options, window_flags) { connect(this, &DockWidget::isOpenChanged, this, &DockWidget::openStateChanged); } void DockWidget::openStateChanged(bool open) { // The LayoutSaver class will close a bunch of dock widgets. We only want to // delete the dock widgets when they're being closed by the user. if (KDDockWidgets::LayoutSaver::restoreInProgress()) return; if (!open && g_debugger_window) g_debugger_window->dockManager().destroyDebuggerView(uniqueName()); } // ***************************************************************************** DockTitleBar::DockTitleBar(KDDockWidgets::Core::TitleBar* controller, KDDockWidgets::Core::View* parent) : KDDockWidgets::QtWidgets::TitleBar(controller, parent) { } void DockTitleBar::mouseDoubleClickEvent(QMouseEvent* event) { if (g_debugger_window && !g_debugger_window->dockManager().isLayoutLocked()) KDDockWidgets::QtWidgets::TitleBar::mouseDoubleClickEvent(event); else event->ignore(); } // ***************************************************************************** DockStack::DockStack(KDDockWidgets::Core::Stack* controller, QWidget* parent) : KDDockWidgets::QtWidgets::Stack(controller, parent) { } void DockStack::init() { KDDockWidgets::QtWidgets::Stack::init(); if (g_debugger_window) { bool locked = g_debugger_window->dockManager().isLayoutLocked(); setTabsClosable(!locked); } } void DockStack::mouseDoubleClickEvent(QMouseEvent* ev) { if (g_debugger_window && !g_debugger_window->dockManager().isLayoutLocked()) KDDockWidgets::QtWidgets::Stack::mouseDoubleClickEvent(ev); else ev->ignore(); } // ***************************************************************************** DockTabBar::DockTabBar(KDDockWidgets::Core::TabBar* controller, QWidget* parent) : KDDockWidgets::QtWidgets::TabBar(controller, parent) { setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &DockTabBar::customContextMenuRequested, this, &DockTabBar::openContextMenu); // The constructor of KDDockWidgets::QtWidgets::TabBar makes a QProxyStyle // that ends up taking ownerhsip of the style for the entire application! if (QProxyStyle* proxy_style = qobject_cast(style())) { proxy_style->baseStyle()->setParent(qApp); proxy_style->setBaseStyle(QStyleFactory::create(qApp->style()->name())); } } void DockTabBar::openContextMenu(QPoint pos) { if (!g_debugger_window) return; int tab_index = tabAt(pos); // Filter out the placeholder widget displayed when there are no layouts. auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; size_t dock_widgets_of_type = g_debugger_window->dockManager().countDebuggerViewsOfType( widget->metaObject()->className()); QMenu* menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); QAction* rename_action = menu->addAction(tr("Rename")); connect(rename_action, &QAction::triggered, this, [this, tab_index]() { if (!g_debugger_window) return; auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; bool ok; QString new_name = QInputDialog::getText( this, tr("Rename Window"), tr("New name:"), QLineEdit::Normal, widget->displayNameWithoutSuffix(), &ok); if (!ok) return; if (!widget->setCustomDisplayName(new_name)) { QMessageBox::warning(this, tr("Invalid Name"), tr("The specified name is too long.")); return; } g_debugger_window->dockManager().updateDockWidgetTitles(); }); QAction* reset_name_action = menu->addAction(tr("Reset Name")); reset_name_action->setEnabled(!widget->customDisplayName().isEmpty()); connect(reset_name_action, &QAction::triggered, this, [this, tab_index] { if (!g_debugger_window) return; auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; widget->setCustomDisplayName(QString()); g_debugger_window->dockManager().updateDockWidgetTitles(); }); QAction* primary_action = menu->addAction(tr("Primary")); primary_action->setCheckable(true); primary_action->setChecked(widget->isPrimary()); primary_action->setEnabled(dock_widgets_of_type > 1); connect(primary_action, &QAction::triggered, this, [this, tab_index](bool checked) { if (!g_debugger_window) return; auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; g_debugger_window->dockManager().setPrimaryDebuggerView(widget, checked); }); QMenu* set_target_menu = menu->addMenu(tr("Set Target")); QActionGroup* set_target_group = new QActionGroup(menu); set_target_group->setExclusive(true); for (BreakPointCpu cpu : DEBUG_CPUS) { const char* long_cpu_name = DebugInterface::longCpuName(cpu); const char* cpu_name = DebugInterface::cpuName(cpu); QString text = tr("%1 (%2)").arg(long_cpu_name).arg(cpu_name); QAction* cpu_action = set_target_menu->addAction(text); cpu_action->setCheckable(true); cpu_action->setChecked(widget->cpuOverride().has_value() && *widget->cpuOverride() == cpu); connect(cpu_action, &QAction::triggered, this, [this, tab_index, cpu]() { setCpuOverrideForTab(tab_index, cpu); }); set_target_group->addAction(cpu_action); } set_target_menu->addSeparator(); QAction* inherit_action = set_target_menu->addAction(tr("Inherit From Layout")); inherit_action->setCheckable(true); inherit_action->setChecked(!widget->cpuOverride().has_value()); connect(inherit_action, &QAction::triggered, this, [this, tab_index]() { setCpuOverrideForTab(tab_index, std::nullopt); }); set_target_group->addAction(inherit_action); QAction* close_action = menu->addAction(tr("Close")); connect(close_action, &QAction::triggered, this, [this, tab_index]() { if (!g_debugger_window) return; auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; g_debugger_window->dockManager().destroyDebuggerView(widget->uniqueName()); }); menu->popup(mapToGlobal(pos)); } void DockTabBar::setCpuOverrideForTab(int tab_index, std::optional cpu_override) { if (!g_debugger_window) return; auto [widget, controller, view] = widgetsFromTabIndex(tab_index); if (!widget) return; if (!widget->setCpuOverride(cpu_override)) g_debugger_window->dockManager().recreateDebuggerView(view->uniqueName()); g_debugger_window->dockManager().updateDockWidgetTitles(); } DockTabBar::WidgetsFromTabIndexResult DockTabBar::widgetsFromTabIndex(int tab_index) { KDDockWidgets::Core::TabBar* tab_bar_controller = asController(); if (!tab_bar_controller) return {}; KDDockWidgets::Core::DockWidget* dock_controller = tab_bar_controller->dockWidgetAt(tab_index); if (!dock_controller) return {}; auto dock_view = static_cast(dock_controller->view()); DebuggerView* widget = qobject_cast(dock_view->widget()); if (!widget) return {}; return {widget, dock_controller, dock_view}; } void DockTabBar::mouseDoubleClickEvent(QMouseEvent* event) { if (g_debugger_window && !g_debugger_window->dockManager().isLayoutLocked()) KDDockWidgets::QtWidgets::TabBar::mouseDoubleClickEvent(event); else event->ignore(); }