Debugger: Allow having multiple dock widgets of the same type

This commit is contained in:
chaoticgd 2025-02-23 12:44:38 +00:00 committed by Ty
parent c9ac4960bc
commit 6fe97b42c3
22 changed files with 703 additions and 241 deletions

View File

@ -11,7 +11,7 @@
#include <QtGui/QClipboard>
BreakpointWidget::BreakpointWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, DISALLOW_MULTIPLE_INSTANCES)
, m_model(new BreakpointModel(cpu()))
{
m_ui.setupUi(this);

View File

@ -11,6 +11,16 @@
#include "DebugTools/DebugInterface.h"
#include "common/Assertions.h"
#include "common/Console.h"
DebuggerWidget::DebuggerWidget(const DebuggerWidgetParameters& parameters, u32 flags)
: QWidget(parameters.parent)
, m_unique_name(parameters.unique_name)
, m_cpu(parameters.cpu)
, m_cpu_override(parameters.cpu_override)
, m_flags(flags)
{
}
DebugInterface& DebuggerWidget::cpu() const
{
@ -21,13 +31,49 @@ DebugInterface& DebuggerWidget::cpu() const
return *m_cpu;
}
QString DebuggerWidget::displayName()
QString DebuggerWidget::uniqueName() const
{
auto description = DockTables::DEBUGGER_WIDGETS.find(metaObject()->className());
if (description == DockTables::DEBUGGER_WIDGETS.end())
return QString();
return m_unique_name;
}
return QCoreApplication::translate("DebuggerWidget", description->second.display_name);
QString DebuggerWidget::displayName() const
{
QString name = displayNameWithoutSuffix();
// If there are multiple debugger widgets of the same name, append a number
// to the display name.
if (m_display_name_suffix_number.has_value())
name = tr("%1 #%2").arg(name).arg(*m_display_name_suffix_number);
if (m_cpu_override)
name = tr("%1 (%2)").arg(name).arg(DebugInterface::cpuName(*m_cpu_override));
return name;
}
QString DebuggerWidget::displayNameWithoutSuffix() const
{
return m_translated_display_name;
}
QString DebuggerWidget::customDisplayName() const
{
return m_custom_display_name;
}
void DebuggerWidget::setCustomDisplayName(QString display_name)
{
m_custom_display_name = display_name;
}
bool DebuggerWidget::isPrimary() const
{
return m_is_primary;
}
void DebuggerWidget::setPrimary(bool is_primary)
{
m_is_primary = is_primary;
}
bool DebuggerWidget::setCpu(DebugInterface& new_cpu)
@ -72,10 +118,24 @@ bool DebuggerWidget::acceptsEventType(const char* event_type)
void DebuggerWidget::toJson(JsonValueWrapper& json)
{
std::string custom_display_name_str = m_custom_display_name.toStdString();
rapidjson::Value custom_display_name;
custom_display_name.SetString(custom_display_name_str.c_str(), custom_display_name_str.size(), json.allocator());
json.value().AddMember("customDisplayName", custom_display_name, json.allocator());
json.value().AddMember("isPrimary", m_is_primary, json.allocator());
}
bool DebuggerWidget::fromJson(JsonValueWrapper& json)
{
auto custom_display_name = json.value().FindMember("customDisplayName");
if (custom_display_name != json.value().MemberEnd() && custom_display_name->value.IsString())
m_custom_display_name = QString(custom_display_name->value.GetString());
auto is_primary = json.value().FindMember("isPrimary");
if (is_primary != json.value().MemberEnd() && is_primary->value.IsBool())
m_is_primary = is_primary->value.GetBool();
return true;
}
@ -97,6 +157,37 @@ void DebuggerWidget::switchToThisTab()
g_debugger_window->dockManager().switchToDebuggerWidget(this);
}
bool DebuggerWidget::supportsMultipleInstances()
{
return !(m_flags & DISALLOW_MULTIPLE_INSTANCES);
}
void DebuggerWidget::retranslateDisplayName()
{
if (!m_custom_display_name.isEmpty())
{
m_translated_display_name = m_custom_display_name;
}
else
{
auto description = DockTables::DEBUGGER_WIDGETS.find(metaObject()->className());
if (description != DockTables::DEBUGGER_WIDGETS.end())
m_translated_display_name = QCoreApplication::translate("DebuggerWidget", description->second.display_name);
else
m_translated_display_name = QString();
}
}
std::optional<int> DebuggerWidget::displayNameSuffixNumber() const
{
return m_display_name_suffix_number;
}
void DebuggerWidget::setDisplayNameSuffixNumber(std::optional<int> suffix_number)
{
m_display_name_suffix_number = suffix_number;
}
void DebuggerWidget::goToInDisassembler(u32 address, bool switch_to_tab)
{
DebuggerEvents::GoToAddress event;
@ -115,20 +206,17 @@ void DebuggerWidget::goToInMemoryView(u32 address, bool switch_to_tab)
DebuggerWidget::sendEvent(std::move(event));
}
DebuggerWidget::DebuggerWidget(const DebuggerWidgetParameters& parameters)
: QWidget(parameters.parent)
, m_cpu(parameters.cpu)
, m_cpu_override(parameters.cpu_override)
{
}
void DebuggerWidget::sendEventImplementation(const DebuggerEvents::Event& event)
{
if (!g_debugger_window)
return;
for (const auto& [unique_name, widget] : g_debugger_window->dockManager().debuggerWidgets())
if (widget->handleEvent(event))
if (widget->isPrimary() && widget->handleEvent(event))
return;
for (const auto& [unique_name, widget] : g_debugger_window->dockManager().debuggerWidgets())
if (!widget->isPrimary() && widget->handleEvent(event))
return;
}
@ -157,6 +245,13 @@ std::vector<QAction*> DebuggerWidget::createEventActionsImplementation(
if ((!skip_self || widget != this) && widget->acceptsEventType(event_type))
receivers.emplace_back(widget);
std::sort(receivers.begin(), receivers.end(), [&](const DebuggerWidget* lhs, const DebuggerWidget* rhs) {
if (lhs->displayNameWithoutSuffix() == rhs->displayNameWithoutSuffix())
return lhs->displayNameSuffixNumber() < rhs->displayNameSuffixNumber();
return lhs->displayNameWithoutSuffix() < rhs->displayNameWithoutSuffix();
});
QMenu* submenu = nullptr;
if (receivers.size() > max_top_level_actions)
{

View File

@ -15,6 +15,7 @@ class JsonValueWrapper;
// Container for variables to be passed to the constructor of DebuggerWidget.
struct DebuggerWidgetParameters
{
QString unique_name;
DebugInterface* cpu = nullptr;
std::optional<BreakPointCpu> cpu_override;
QWidget* parent = nullptr;
@ -26,8 +27,17 @@ class DebuggerWidget : public QWidget
Q_OBJECT
public:
QString uniqueName() const;
// Get the translated name that should be displayed for this widget.
QString displayName();
QString displayName() const;
QString displayNameWithoutSuffix() const;
QString customDisplayName() const;
void setCustomDisplayName(QString display_name);
bool isPrimary() const;
void setPrimary(bool is_primary);
// Get the effective debug interface associated with this particular widget
// if it's set, otherwise return the one associated with the layout that
@ -133,11 +143,25 @@ public:
void switchToThisTab();
bool supportsMultipleInstances();
void retranslateDisplayName();
std::optional<int> displayNameSuffixNumber() const;
void setDisplayNameSuffixNumber(std::optional<int> suffix_number);
static void goToInDisassembler(u32 address, bool switch_to_tab);
static void goToInMemoryView(u32 address, bool switch_to_tab);
protected:
DebuggerWidget(const DebuggerWidgetParameters& parameters);
enum Flags
{
NO_DEBUGGER_FLAGS = 0,
// Prevent the user from opening multiple dock widgets of this type.
DISALLOW_MULTIPLE_INSTANCES = 1 << 0
};
DebuggerWidget(const DebuggerWidgetParameters& parameters, u32 flags);
private:
static void sendEventImplementation(const DebuggerEvents::Event& event);
@ -151,7 +175,22 @@ private:
const char* action_prefix,
std::function<const DebuggerEvents::Event*()> event_func);
QString m_unique_name;
// A user-defined name, or an empty string if no name was specified so that
// the default names can be retranslated on the fly.
QString m_custom_display_name;
QString m_translated_display_name;
std::optional<int> m_display_name_suffix_number;
// Primary debugger widgets will be chosen to handle events first. For
// example, clicking on an address to go to it in the primary memory view.
bool m_is_primary = false;
DebugInterface* m_cpu;
std::optional<BreakPointCpu> m_cpu_override;
u32 m_flags;
std::multimap<std::string, std::function<bool(const DebuggerEvents::Event&)>> m_event_handlers;
};

View File

@ -20,7 +20,7 @@
using namespace QtUtils;
DisassemblyWidget::DisassemblyWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
{
m_ui.setupUi(this);

View File

@ -47,14 +47,20 @@ DockLayout::DockLayout(
{
for (size_t i = 0; i < default_layout.widgets.size(); i++)
{
auto iterator = DockTables::DEBUGGER_WIDGETS.find(QString::fromStdString(default_layout.widgets[i].type));
auto iterator = DockTables::DEBUGGER_WIDGETS.find(default_layout.widgets[i].type);
pxAssertRel(iterator != DockTables::DEBUGGER_WIDGETS.end(), "Invalid default layout.");
const DockTables::DebuggerWidgetDescription& dock_description = iterator->second;
DebuggerWidgetParameters parameters;
parameters.unique_name = generateNewUniqueName(default_layout.widgets[i].type.c_str());
parameters.cpu = &DebugInterface::get(cpu);
if (parameters.unique_name.isEmpty())
continue;
DebuggerWidget* widget = dock_description.create_widget(parameters);
m_widgets.emplace(QString::fromStdString(default_layout.widgets[i].type), widget);
widget->setPrimary(true);
m_widgets.emplace(parameters.unique_name, widget);
}
save(index);
@ -81,6 +87,7 @@ DockLayout::DockLayout(
: m_name(name)
, m_cpu(cpu)
, m_is_default(is_default)
, m_next_unique_name(layout_to_clone.m_next_unique_name)
, m_base_layout(layout_to_clone.m_base_layout)
, m_toolbars(layout_to_clone.m_toolbars)
, m_geometry(layout_to_clone.m_geometry)
@ -92,9 +99,13 @@ DockLayout::DockLayout(
continue;
DebuggerWidgetParameters parameters;
parameters.unique_name = unique_name;
parameters.cpu = &DebugInterface::get(cpu);
parameters.cpu_override = widget_to_clone->cpuOverride();
DebuggerWidget* new_widget = widget_description->second.create_widget(parameters);
new_widget->setCustomDisplayName(widget_to_clone->customDisplayName());
new_widget->setPrimary(widget_to_clone->isPrimary());
m_widgets.emplace(unique_name, new_widget);
}
@ -155,8 +166,8 @@ void DockLayout::setCpu(BreakPointCpu cpu)
void DockLayout::freeze()
{
pxAssert(!m_is_frozen);
m_is_frozen = true;
pxAssert(m_is_active);
m_is_active = false;
if (g_debugger_window)
m_toolbars = g_debugger_window->saveState();
@ -178,8 +189,8 @@ void DockLayout::freeze()
void DockLayout::thaw()
{
pxAssert(m_is_frozen);
m_is_frozen = false;
pxAssert(!m_is_active);
m_is_active = true;
if (!g_debugger_window)
return;
@ -204,26 +215,26 @@ void DockLayout::thaw()
{
// This is a newly created layout with no geometry information.
setupDefaultLayout();
return;
}
// Restore the geometry of the dock widgets we just recreated.
KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow);
if (!saver.restoreLayout(m_geometry))
else
{
// We've failed to restore the geometry, so just tear down whatever dock
// widgets may exist and then setup the default layout.
for (KDDockWidgets::Core::DockWidget* dock : KDDockWidgets::DockRegistry::self()->dockwidgets())
// Create all the dock widgets.
KDDockWidgets::LayoutSaver saver(KDDockWidgets::RestoreOption_RelativeToMainWindow);
if (!saver.restoreLayout(m_geometry))
{
// Make sure the dock widget releases ownership of its content.
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock->view());
view->setWidget(new QWidget());
// We've failed to restore the geometry, so just tear down whatever
// dock widgets may exist and then setup the default layout.
for (KDDockWidgets::Core::DockWidget* dock : KDDockWidgets::DockRegistry::self()->dockwidgets())
{
// Make sure the dock widget releases ownership of its content.
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock->view());
view->setWidget(new QWidget());
delete dock;
delete dock;
}
setupDefaultLayout();
}
setupDefaultLayout();
return;
}
// Check that all the dock widgets have been restored correctly.
@ -242,16 +253,19 @@ void DockLayout::thaw()
for (const QString& unique_name : orphaned_debugger_widgets)
{
auto widget_iterator = m_widgets.find(unique_name);
pxAssert(widget_iterator != m_widgets.end());
setPrimaryDebuggerWidget(widget_iterator->second.get(), false);
delete widget_iterator->second.get();
m_widgets.erase(widget_iterator);
}
retranslateDockWidgets();
updateDockWidgetTitles();
}
KDDockWidgets::Core::DockWidget* DockLayout::createDockWidget(const QString& name)
{
pxAssert(!m_is_frozen);
pxAssert(m_is_active);
pxAssert(KDDockWidgets::LayoutSaver::restoreInProgress());
auto widget_iterator = m_widgets.find(name);
@ -260,6 +274,7 @@ KDDockWidgets::Core::DockWidget* DockLayout::createDockWidget(const QString& nam
DebuggerWidget* widget = widget_iterator->second;
pxAssert(widget);
pxAssert(widget->uniqueName() == name);
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(
KDDockWidgets::Config::self().viewFactory()->createDockWidget(name));
@ -268,139 +283,132 @@ KDDockWidgets::Core::DockWidget* DockLayout::createDockWidget(const QString& nam
return view->asController<KDDockWidgets::Core::DockWidget>();
}
void DockLayout::retranslateDockWidgets()
void DockLayout::updateDockWidgetTitles()
{
for (KDDockWidgets::Core::DockWidget* widget : KDDockWidgets::DockRegistry::self()->dockwidgets())
retranslateDockWidget(widget);
}
void DockLayout::retranslateDockWidget(KDDockWidgets::Core::DockWidget* dock_widget)
{
pxAssert(!m_is_frozen);
auto widget_iterator = m_widgets.find(dock_widget->uniqueName());
if (widget_iterator == m_widgets.end())
if (!m_is_active)
return;
DebuggerWidget* widget = widget_iterator->second.get();
if (!widget)
return;
;
std::optional<BreakPointCpu> cpu_override = widget->cpuOverride();
// Translate default debugger widget names.
for (auto& [unique_name, widget] : m_widgets)
widget->retranslateDisplayName();
if (cpu_override.has_value())
// Determine if any widgets have duplicate display names.
std::map<QString, std::vector<DebuggerWidget*>> display_name_to_widgets;
for (auto& [unique_name, widget] : m_widgets)
display_name_to_widgets[widget->displayNameWithoutSuffix()].emplace_back(widget.get());
for (auto& [display_name, widgets] : display_name_to_widgets)
{
const char* cpu_name = DebugInterface::cpuName(*cpu_override);
dock_widget->setTitle(QString("%1 (%2)").arg(widget->displayName()).arg(cpu_name));
std::sort(widgets.begin(), widgets.end(),
[&](const DebuggerWidget* lhs, const DebuggerWidget* rhs) {
return lhs->uniqueName() < rhs->uniqueName();
});
for (size_t i = 0; i < widgets.size(); i++)
{
std::optional<int> suffix_number;
if (widgets.size() != 1)
suffix_number = static_cast<int>(i + 1);
widgets[i]->setDisplayNameSuffixNumber(suffix_number);
}
}
else
// Propagate the new names from the debugger widgets to the dock widgets.
for (auto& [unique_name, widget] : m_widgets)
{
dock_widget->setTitle(std::move(widget->displayName()));
auto [controller, view] = DockUtils::dockWidgetFromName(widget->uniqueName());
if (!controller)
continue;
controller->setTitle(widget->displayName());
}
}
void DockLayout::dockWidgetClosed(KDDockWidgets::Core::DockWidget* dock_widget)
{
// 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;
auto debugger_widget_iterator = m_widgets.find(dock_widget->uniqueName());
if (debugger_widget_iterator == m_widgets.end())
return;
m_widgets.erase(debugger_widget_iterator);
dock_widget->deleteLater();
}
const std::map<QString, QPointer<DebuggerWidget>>& DockLayout::debuggerWidgets()
{
return m_widgets;
}
bool DockLayout::hasDebuggerWidget(QString unique_name)
bool DockLayout::hasDebuggerWidget(const QString& unique_name)
{
return m_widgets.find(unique_name) != m_widgets.end();
}
void DockLayout::toggleDebuggerWidget(QString unique_name)
size_t DockLayout::countDebuggerWidgetsOfType(const char* type)
{
pxAssert(!m_is_frozen);
size_t count = 0;
for (const auto& [unique_name, widget] : m_widgets)
{
if (strcmp(widget->metaObject()->className(), type) == 0)
count++;
}
return count;
}
void DockLayout::createDebuggerWidget(const std::string& type)
{
pxAssert(m_is_active);
if (!g_debugger_window)
return;
auto debugger_widget_iterator = m_widgets.find(unique_name);
auto [controller, view] = DockUtils::dockWidgetFromName(unique_name);
auto description_iterator = DockTables::DEBUGGER_WIDGETS.find(type);
pxAssert(description_iterator != DockTables::DEBUGGER_WIDGETS.end());
if (debugger_widget_iterator == m_widgets.end())
{
// Create the dock widget.
if (controller)
return;
const DockTables::DebuggerWidgetDescription& description = description_iterator->second;
auto description_iterator = DockTables::DEBUGGER_WIDGETS.find(unique_name);
if (description_iterator == DockTables::DEBUGGER_WIDGETS.end())
return;
DebuggerWidgetParameters parameters;
parameters.unique_name = generateNewUniqueName(type.c_str());
parameters.cpu = &DebugInterface::get(m_cpu);
const DockTables::DebuggerWidgetDescription& description = description_iterator->second;
if (parameters.unique_name.isEmpty())
return;
DebuggerWidgetParameters parameters;
parameters.cpu = &DebugInterface::get(m_cpu);
DebuggerWidget* widget = description.create_widget(parameters);
m_widgets.emplace(unique_name, widget);
DebuggerWidget* widget = description.create_widget(parameters);
m_widgets.emplace(parameters.unique_name, widget);
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(
KDDockWidgets::Config::self().viewFactory()->createDockWidget(unique_name));
view->setWidget(widget);
setPrimaryDebuggerWidget(widget, countDebuggerWidgetsOfType(type.c_str()) == 0);
KDDockWidgets::Core::DockWidget* controller = view->asController<KDDockWidgets::Core::DockWidget>();
if (!controller)
{
delete view;
return;
}
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(
KDDockWidgets::Config::self().viewFactory()->createDockWidget(widget->uniqueName()));
view->setWidget(widget);
DockUtils::insertDockWidgetAtPreferredLocation(controller, description.preferred_location, g_debugger_window);
retranslateDockWidget(controller);
}
else
{
// Delete the dock widget.
if (!controller)
return;
KDDockWidgets::Core::DockWidget* controller = view->asController<KDDockWidgets::Core::DockWidget>();
pxAssert(controller);
m_widgets.erase(debugger_widget_iterator);
delete controller;
}
DockUtils::insertDockWidgetAtPreferredLocation(controller, description.preferred_location, g_debugger_window);
updateDockWidgetTitles();
}
void DockLayout::recreateDebuggerWidget(QString unique_name)
void DockLayout::recreateDebuggerWidget(const QString& unique_name)
{
pxAssert(!m_is_frozen);
pxAssert(m_is_active);
auto [controller, view] = DockUtils::dockWidgetFromName(unique_name);
if (!controller || !view)
return;
pxAssert(controller);
pxAssert(view);
auto debugger_widget_iterator = m_widgets.find(unique_name);
if (debugger_widget_iterator == m_widgets.end())
return;
pxAssert(debugger_widget_iterator != m_widgets.end());
DebuggerWidget* old_debugger_widget = debugger_widget_iterator->second;
pxAssert(old_debugger_widget == view->widget());
auto description_iterator = DockTables::DEBUGGER_WIDGETS.find(old_debugger_widget->metaObject()->className());
if (description_iterator == DockTables::DEBUGGER_WIDGETS.end())
return;
pxAssert(description_iterator != DockTables::DEBUGGER_WIDGETS.end());
const DockTables::DebuggerWidgetDescription& description = description_iterator->second;
DebuggerWidgetParameters parameters;
parameters.unique_name = old_debugger_widget->uniqueName();
parameters.cpu = &DebugInterface::get(m_cpu);
parameters.cpu_override = old_debugger_widget->cpuOverride();
DebuggerWidget* new_debugger_widget = description.create_widget(parameters);
new_debugger_widget->setCustomDisplayName(old_debugger_widget->customDisplayName());
new_debugger_widget->setPrimary(old_debugger_widget->isPrimary());
debugger_widget_iterator->second = new_debugger_widget;
view->setWidget(new_debugger_widget);
@ -408,6 +416,77 @@ void DockLayout::recreateDebuggerWidget(QString unique_name)
delete old_debugger_widget;
}
void DockLayout::destroyDebuggerWidget(const QString& unique_name)
{
pxAssert(m_is_active);
if (!g_debugger_window)
return;
auto debugger_widget_iterator = m_widgets.find(unique_name);
if (debugger_widget_iterator == m_widgets.end())
return;
setPrimaryDebuggerWidget(debugger_widget_iterator->second.get(), false);
delete debugger_widget_iterator->second.get();
m_widgets.erase(debugger_widget_iterator);
auto [controller, view] = DockUtils::dockWidgetFromName(unique_name);
if (!controller)
return;
controller->deleteLater();
updateDockWidgetTitles();
}
void DockLayout::setPrimaryDebuggerWidget(DebuggerWidget* widget, bool is_primary)
{
bool present = false;
for (auto& [unique_name, test_widget] : m_widgets)
{
if (test_widget.get() == widget)
{
present = true;
break;
}
}
if (!present)
return;
if (is_primary)
{
// Set the passed widget as the primary widget.
for (auto& [unique_name, test_widget] : m_widgets)
{
if (strcmp(test_widget->metaObject()->className(), widget->metaObject()->className()) == 0)
{
test_widget->setPrimary(test_widget.get() == widget);
}
}
}
else if (widget->isPrimary())
{
// Set an arbitrary widget as the primary widget.
bool next = true;
for (auto& [unique_name, test_widget] : m_widgets)
{
if (test_widget != widget &&
strcmp(test_widget->metaObject()->className(), widget->metaObject()->className()) == 0)
{
test_widget->setPrimary(next);
next = false;
}
}
// If we haven't set another widget as the primary one we can't make
// this one not the primary one.
if (!next)
widget->setPrimary(false);
}
}
void DockLayout::deleteFile()
{
if (m_layout_file_path.empty())
@ -416,12 +495,13 @@ 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 (!g_debugger_window)
return false;
if (!m_is_frozen)
if (m_is_active)
{
m_toolbars = g_debugger_window->saveState();
@ -451,6 +531,7 @@ bool DockLayout::save(DockLayout::Index layout_index)
json.AddMember("target", rapidjson::Value().SetString(cpu_name, strlen(cpu_name)), json.GetAllocator());
json.AddMember("index", static_cast<int>(layout_index), json.GetAllocator());
json.AddMember("isDefault", m_is_default, json.GetAllocator());
json.AddMember("nextUniqueName", m_next_unique_name, json.GetAllocator());
if (!m_base_layout.empty())
{
@ -545,7 +626,7 @@ void DockLayout::load(
LoadResult& result,
DockLayout::Index& index_last_session)
{
pxAssert(m_is_frozen);
pxAssert(!m_is_active);
result = SUCCESS;
@ -631,6 +712,10 @@ void DockLayout::load(
if (is_default != json.MemberEnd() && is_default->value.IsBool())
m_is_default = is_default->value.GetBool();
auto next_unique_name = json.FindMember("nextUniqueName");
if (next_unique_name != json.MemberBegin() && next_unique_name->value.IsInt())
m_next_unique_name = next_unique_name->value.GetInt();
auto base_layout = json.FindMember("baseLayout");
if (base_layout != json.MemberEnd() && base_layout->value.IsString())
m_base_layout = base_layout->value.GetString();
@ -648,6 +733,10 @@ void DockLayout::load(
if (unique_name == object.MemberEnd() || !unique_name->value.IsString())
continue;
auto widgets_iterator = m_widgets.find(unique_name->value.GetString());
if (widgets_iterator != m_widgets.end())
continue;
auto type = object.FindMember("type");
if (type == object.MemberEnd() || !type->value.IsString())
continue;
@ -667,8 +756,10 @@ void DockLayout::load(
}
DebuggerWidgetParameters parameters;
parameters.unique_name = unique_name->value.GetString();
parameters.cpu = &DebugInterface::get(m_cpu);
parameters.cpu_override = cpu_override;
DebuggerWidget* widget = description->second.create_widget(parameters);
JsonValueWrapper wrapper(object, json.GetAllocator());
@ -693,13 +784,44 @@ void DockLayout::load(
}
m_layout_file_path = path;
validatePrimaryDebuggerWidgets();
}
void DockLayout::validatePrimaryDebuggerWidgets()
{
std::map<std::string, std::vector<DebuggerWidget*>> type_to_widgets;
for (const auto& [unique_name, widget] : m_widgets)
type_to_widgets[widget->metaObject()->className()].emplace_back(widget.get());
for (auto& [type, widgets] : type_to_widgets)
{
u32 primary_widgets = 0;
// Make sure at most one widget is marked as primary.
for (DebuggerWidget* widget : widgets)
{
if (widget->isPrimary())
{
if (primary_widgets != 0)
widget->setPrimary(false);
primary_widgets++;
}
}
// If none of the widgets were marked as primary, just set the first one
// as the primary one.
if (primary_widgets == 0)
widgets[0]->setPrimary(true);
}
}
void DockLayout::setupDefaultLayout()
{
pxAssert(!m_is_frozen);
pxAssert(m_is_active);
if (m_base_layout.empty() || !g_debugger_window)
if (!g_debugger_window)
return;
const DockTables::DefaultDockLayout* base_layout = DockTables::defaultLayout(m_base_layout);
@ -713,15 +835,16 @@ void DockLayout::setupDefaultLayout()
const DockTables::DefaultDockGroupDescription& group =
base_layout->groups[static_cast<u32>(dock_description.group)];
auto widget_iterator = m_widgets.find(QString::fromStdString(dock_description.type));
if (widget_iterator == m_widgets.end())
DebuggerWidget* widget = nullptr;
for (auto& [unique_name, test_widget] : m_widgets)
if (test_widget->metaObject()->className() == dock_description.type)
widget = test_widget;
if (!widget)
continue;
const QString& unique_name = widget_iterator->first;
DebuggerWidget* widget = widget_iterator->second;
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(
KDDockWidgets::Config::self().viewFactory()->createDockWidget(unique_name));
KDDockWidgets::Config::self().viewFactory()->createDockWidget(widget->uniqueName()));
view->setWidget(widget);
if (!groups[static_cast<u32>(dock_description.group)])
@ -742,6 +865,21 @@ void DockLayout::setupDefaultLayout()
for (KDDockWidgets::Core::Group* group : KDDockWidgets::DockRegistry::self()->groups())
group->setCurrentTabIndex(0);
retranslateDockWidgets();
}
QString DockLayout::generateNewUniqueName(const char* type)
{
QString name;
do
{
if (m_next_unique_name == INT_MAX)
return QString();
// Produce unique names that will lexicographically sort in the order
// they were allocated. This ensures the #1, #2, etc suffixes for dock
// widgets with conflicting names will be assigned in the correct order.
name = QStringLiteral("%1-%2").arg(m_next_unique_name, 16, 10, QLatin1Char('0')).arg(type);
m_next_unique_name++;
} while (hasDebuggerWidget(name));
return name;
}

View File

@ -92,14 +92,15 @@ public:
void thaw();
KDDockWidgets::Core::DockWidget* createDockWidget(const QString& name);
void retranslateDockWidgets();
void retranslateDockWidget(KDDockWidgets::Core::DockWidget* dock_widget);
void dockWidgetClosed(KDDockWidgets::Core::DockWidget* dock_widget);
void updateDockWidgetTitles();
const std::map<QString, QPointer<DebuggerWidget>>& debuggerWidgets();
bool hasDebuggerWidget(QString unique_name);
void toggleDebuggerWidget(QString unique_name);
void recreateDebuggerWidget(QString unique_name);
bool hasDebuggerWidget(const QString& unique_name);
size_t countDebuggerWidgetsOfType(const char* type);
void createDebuggerWidget(const std::string& type);
void recreateDebuggerWidget(const QString& unique_name);
void destroyDebuggerWidget(const QString& unique_name);
void setPrimaryDebuggerWidget(DebuggerWidget* widget, bool is_primary);
void deleteFile();
@ -111,8 +112,13 @@ private:
DockLayout::LoadResult& result,
DockLayout::Index& index_last_session);
// Make sure there is only a single primary debugger widget of each type.
void validatePrimaryDebuggerWidgets();
void setupDefaultLayout();
QString generateNewUniqueName(const char* type);
// The name displayed in the user interface. Also used to determine the
// file name for the layout file.
std::string m_name;
@ -124,6 +130,9 @@ private:
// Is this one of the default layouts?
bool m_is_default = false;
// A counter used to generate new unique names for dock widgets.
int m_next_unique_name = 0;
// The name of the default layout which this layout was based on. This will
// be used if the m_geometry variable above is empty.
std::string m_base_layout;
@ -145,7 +154,7 @@ private:
// exists exists on disk, or empty if no such file exists.
std::string m_layout_file_path;
// If this layout is the currently selected layout this will be false,
// otherwise it will be true.
bool m_is_frozen = true;
// If this layout is the currently selected layout this will be true,
// otherwise it will be false.
bool m_is_active = false;
};

View File

@ -315,17 +315,119 @@ void DockManager::createWindowsMenu(QMenu* menu)
DockLayout& layout = m_layouts.at(m_current_layout);
for (const auto& [type, desc] : DockTables::DEBUGGER_WIDGETS)
// Create a menu that allows for multiple dock widgets of the same type to
// be opened.
QMenu* add_another_menu = menu->addMenu(tr("Add Another..."));
std::vector<DebuggerWidget*> add_another_widgets;
std::set<std::string> add_another_types;
for (const auto& [unique_name, widget] : layout.debuggerWidgets())
{
std::string type = widget->metaObject()->className();
if (widget->supportsMultipleInstances() && !add_another_types.contains(type))
{
add_another_widgets.emplace_back(widget);
add_another_types.emplace(type);
}
}
std::sort(add_another_widgets.begin(), add_another_widgets.end(),
[](const DebuggerWidget* lhs, const DebuggerWidget* rhs) {
if (lhs->displayNameWithoutSuffix() == rhs->displayNameWithoutSuffix())
return lhs->displayNameSuffixNumber() < rhs->displayNameSuffixNumber();
return lhs->displayNameWithoutSuffix() < rhs->displayNameWithoutSuffix();
});
for (DebuggerWidget* widget : add_another_widgets)
{
const char* type = widget->metaObject()->className();
const auto description_iterator = DockTables::DEBUGGER_WIDGETS.find(type);
pxAssert(description_iterator != DockTables::DEBUGGER_WIDGETS.end());
QAction* action = add_another_menu->addAction(description_iterator->second.display_name);
connect(action, &QAction::triggered, this, [this, type]() {
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).createDebuggerWidget(type);
});
}
if (add_another_widgets.empty())
add_another_menu->setDisabled(true);
menu->addSeparator();
struct DebuggerWidgetToggle
{
QString display_name;
std::optional<int> suffix_number;
QAction* action;
};
std::vector<DebuggerWidgetToggle> toggles;
std::set<std::string> toggle_types;
// Create a menu item for each open debugger widget.
for (const auto& [unique_name, widget] : layout.debuggerWidgets())
{
QAction* action = new QAction(menu);
action->setText(QCoreApplication::translate("DebuggerWidget", desc.display_name));
action->setText(widget->displayName());
action->setCheckable(true);
action->setChecked(layout.hasDebuggerWidget(type));
connect(action, &QAction::triggered, this, [&layout, type]() {
layout.toggleDebuggerWidget(type);
action->setChecked(true);
connect(action, &QAction::triggered, this, [this, unique_name]() {
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).destroyDebuggerWidget(unique_name);
});
menu->addAction(action);
DebuggerWidgetToggle& toggle = toggles.emplace_back();
toggle.display_name = widget->displayNameWithoutSuffix();
toggle.suffix_number = widget->displayNameSuffixNumber();
toggle.action = action;
toggle_types.emplace(widget->metaObject()->className());
}
// Create menu items to open debugger widgets without any open instances.
for (const auto& [type, desc] : DockTables::DEBUGGER_WIDGETS)
{
if (!toggle_types.contains(type))
{
QString display_name = QCoreApplication::translate("DebuggerWidget", desc.display_name);
QAction* action = new QAction(menu);
action->setText(display_name);
action->setCheckable(true);
action->setChecked(false);
connect(action, &QAction::triggered, this, [this, type]() {
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).createDebuggerWidget(type);
});
DebuggerWidgetToggle& toggle = toggles.emplace_back();
toggle.display_name = display_name;
toggle.suffix_number = std::nullopt;
toggle.action = action;
}
}
std::sort(toggles.begin(), toggles.end(),
[](const DebuggerWidgetToggle& lhs, const DebuggerWidgetToggle& rhs) {
if (lhs.display_name == rhs.display_name)
return lhs.suffix_number < rhs.suffix_number;
return lhs.display_name < rhs.display_name;
});
for (const DebuggerWidgetToggle& toggle : toggles)
menu->addAction(toggle.action);
}
QWidget* DockManager::createLayoutSwitcher(QWidget* menu_bar)
@ -564,20 +666,12 @@ bool DockManager::hasNameConflict(const std::string& name, DockLayout::Index lay
return false;
}
void DockManager::retranslateDockWidget(KDDockWidgets::Core::DockWidget* dock_widget)
void DockManager::updateDockWidgetTitles()
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).retranslateDockWidget(dock_widget);
}
void DockManager::dockWidgetClosed(KDDockWidgets::Core::DockWidget* dock_widget)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).dockWidgetClosed(dock_widget);
m_layouts.at(m_current_layout).updateDockWidgetTitles();
}
const std::map<QString, QPointer<DebuggerWidget>>& DockManager::debuggerWidgets()
@ -589,7 +683,15 @@ const std::map<QString, QPointer<DebuggerWidget>>& DockManager::debuggerWidgets(
return m_layouts.at(m_current_layout).debuggerWidgets();
}
void DockManager::recreateDebuggerWidget(QString unique_name)
size_t DockManager::countDebuggerWidgetsOfType(const char* type)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return 0;
return m_layouts.at(m_current_layout).countDebuggerWidgetsOfType(type);
}
void DockManager::recreateDebuggerWidget(const QString& unique_name)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
@ -597,6 +699,22 @@ void DockManager::recreateDebuggerWidget(QString unique_name)
m_layouts.at(m_current_layout).recreateDebuggerWidget(unique_name);
}
void DockManager::destroyDebuggerWidget(const QString& unique_name)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).destroyDebuggerWidget(unique_name);
}
void DockManager::setPrimaryDebuggerWidget(DebuggerWidget* widget, bool is_primary)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
return;
m_layouts.at(m_current_layout).setPrimaryDebuggerWidget(widget, is_primary);
}
void DockManager::switchToDebuggerWidget(DebuggerWidget* widget)
{
if (m_current_layout == DockLayout::INVALID_INDEX)
@ -613,6 +731,7 @@ void DockManager::switchToDebuggerWidget(DebuggerWidget* widget)
}
}
bool DockManager::isLayoutLocked()
{
return m_layout_locked;

View File

@ -71,11 +71,13 @@ public:
bool hasNameConflict(const std::string& name, DockLayout::Index layout_index);
void retranslateDockWidget(KDDockWidgets::Core::DockWidget* dock_widget);
void dockWidgetClosed(KDDockWidgets::Core::DockWidget* dock_widget);
void updateDockWidgetTitles();
const std::map<QString, QPointer<DebuggerWidget>>& debuggerWidgets();
void recreateDebuggerWidget(QString unique_name);
size_t countDebuggerWidgetsOfType(const char* type);
void recreateDebuggerWidget(const QString& unique_name);
void destroyDebuggerWidget(const QString& unique_name);
void setPrimaryDebuggerWidget(DebuggerWidget* widget, bool is_primary);
void switchToDebuggerWidget(DebuggerWidget* widget);
bool isLayoutLocked();

View File

@ -34,7 +34,7 @@ using namespace DockUtils;
} \
}
const std::map<QString, DockTables::DebuggerWidgetDescription> DockTables::DEBUGGER_WIDGETS = {
const std::map<std::string, DockTables::DebuggerWidgetDescription> DockTables::DEBUGGER_WIDGETS = {
DEBUGGER_WIDGET(BreakpointWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Breakpoints"), BOTTOM_MIDDLE),
DEBUGGER_WIDGET(DisassemblyWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Disassembly"), TOP_RIGHT),
DEBUGGER_WIDGET(FunctionTreeWidget, QT_TRANSLATE_NOOP("DebuggerWidget", "Functions"), TOP_LEFT),

View File

@ -28,7 +28,7 @@ namespace DockTables
DockUtils::PreferredLocation preferred_location;
};
extern const std::map<QString, DebuggerWidgetDescription> DEBUGGER_WIDGETS;
extern const std::map<std::string, DebuggerWidgetDescription> DEBUGGER_WIDGETS;
enum class DefaultDockGroup
{

View File

@ -8,7 +8,7 @@
#include <kddockwidgets/qtwidgets/DockWidget.h>
#include <kddockwidgets/qtwidgets/Group.h>
DockUtils::DockWidgetPair DockUtils::dockWidgetFromName(QString unique_name)
DockUtils::DockWidgetPair DockUtils::dockWidgetFromName(const QString& unique_name)
{
KDDockWidgets::Vector<QString> names{unique_name};
KDDockWidgets::Vector<KDDockWidgets::Core::DockWidget*> dock_widgets =

View File

@ -15,7 +15,7 @@ namespace DockUtils
KDDockWidgets::QtWidgets::DockWidget* view = nullptr;
};
DockWidgetPair dockWidgetFromName(QString unique_name);
DockWidgetPair dockWidgetFromName(const QString& unique_name);
enum PreferredLocation
{

View File

@ -12,7 +12,9 @@
#include <kddockwidgets/core/TabBar.h>
#include <kddockwidgets/qtwidgets/views/DockWidget.h>
#include <QMenu>
#include <QtGui/QActionGroup>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMenu>
KDDockWidgets::Core::View* DockViewFactory::createDockWidget(
const QString& unique_name,
@ -58,14 +60,13 @@ DockWidget::DockWidget(
void DockWidget::openStateChanged(bool open)
{
auto view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(sender());
KDDockWidgets::Core::DockWidget* controller = view->asController<KDDockWidgets::Core::DockWidget>();
if (!controller)
// 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().dockWidgetClosed(controller);
g_debugger_window->dockManager().destroyDebuggerWidget(uniqueName());
}
// *****************************************************************************
@ -115,104 +116,151 @@ DockTabBar::DockTabBar(KDDockWidgets::Core::TabBar* controller, QWidget* parent)
: KDDockWidgets::QtWidgets::TabBar(controller, parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &DockTabBar::customContextMenuRequested, this, &DockTabBar::contextMenu);
connect(this, &DockTabBar::customContextMenuRequested, this, &DockTabBar::openContextMenu);
}
void DockTabBar::contextMenu(QPoint pos)
void DockTabBar::openContextMenu(QPoint pos)
{
auto tab_bar = qobject_cast<KDDockWidgets::QtWidgets::TabBar*>(sender());
int tab_index = tab_bar->tabAt(pos);
// Filter out the placeholder widget displayed when there are no layouts.
if (!hasDebuggerWidget(tab_index))
if (!g_debugger_window)
return;
QMenu* menu = new QMenu(tr("Dock Widget Menu"), tab_bar);
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().countDebuggerWidgetsOfType(
widget->metaObject()->className());
QMenu* menu = new QMenu(tr("Dock Widget Context Menu"), 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;
widget->setCustomDisplayName(new_name);
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().setPrimaryDebuggerWidget(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 = QString("%1 (%2)").arg(long_cpu_name).arg(cpu_name);
QAction* action = new QAction(text, menu);
connect(action, &QAction::triggered, this, [tab_bar, tab_index, cpu]() {
KDDockWidgets::Core::TabBar* tab_bar_controller = tab_bar->asController<KDDockWidgets::Core::TabBar>();
if (!tab_bar_controller)
return;
KDDockWidgets::Core::DockWidget* dock_controller = tab_bar_controller->dockWidgetAt(tab_index);
if (!dock_controller)
return;
KDDockWidgets::QtWidgets::DockWidget* dock_view =
static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock_controller->view());
DebuggerWidget* widget = qobject_cast<DebuggerWidget*>(dock_view->widget());
if (!widget)
return;
if (!g_debugger_window)
return;
if (!widget->setCpuOverride(cpu))
g_debugger_window->dockManager().recreateDebuggerWidget(dock_view->uniqueName());
g_debugger_window->dockManager().retranslateDockWidget(dock_controller);
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_menu->addAction(action);
set_target_group->addAction(cpu_action);
}
set_target_menu->addSeparator();
QAction* inherit_action = new QAction(tr("Inherit From Layout"), menu);
connect(inherit_action, &QAction::triggered, this, [tab_bar, tab_index]() {
KDDockWidgets::Core::TabBar* tab_bar_controller = tab_bar->asController<KDDockWidgets::Core::TabBar>();
if (!tab_bar_controller)
return;
KDDockWidgets::Core::DockWidget* dock_controller = tab_bar_controller->dockWidgetAt(tab_index);
if (!dock_controller)
return;
KDDockWidgets::QtWidgets::DockWidget* dock_view =
static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock_controller->view());
DebuggerWidget* widget = qobject_cast<DebuggerWidget*>(dock_view->widget());
if (!widget)
return;
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;
if (!widget->setCpuOverride(std::nullopt))
g_debugger_window->dockManager().recreateDebuggerWidget(dock_view->uniqueName());
auto [widget, controller, view] = widgetsFromTabIndex(tab_index);
if (!widget)
return;
g_debugger_window->dockManager().retranslateDockWidget(dock_controller);
g_debugger_window->dockManager().destroyDebuggerWidget(widget->uniqueName());
});
set_target_menu->addAction(inherit_action);
menu->popup(tab_bar->mapToGlobal(pos));
menu->popup(mapToGlobal(pos));
}
bool DockTabBar::hasDebuggerWidget(int tab_index)
void DockTabBar::setCpuOverrideForTab(int tab_index, std::optional<BreakPointCpu> 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().recreateDebuggerWidget(view->uniqueName());
g_debugger_window->dockManager().updateDockWidgetTitles();
}
DockTabBar::WidgetsFromTabIndexResult DockTabBar::widgetsFromTabIndex(int tab_index)
{
KDDockWidgets::Core::TabBar* tab_bar_controller = asController<KDDockWidgets::Core::TabBar>();
if (!tab_bar_controller)
return false;
return {};
KDDockWidgets::Core::DockWidget* dock_controller = tab_bar_controller->dockWidgetAt(tab_index);
if (!dock_controller)
return false;
return {};
auto dock_view = static_cast<KDDockWidgets::QtWidgets::DockWidget*>(dock_controller->view());
DebuggerWidget* widget = qobject_cast<DebuggerWidget*>(dock_view->widget());
if (!widget)
return false;
return {};
return true;
return {widget, dock_controller, dock_view};
}
void DockTabBar::mouseDoubleClickEvent(QMouseEvent* ev)

View File

@ -9,6 +9,9 @@
#include <kddockwidgets/qtwidgets/views/TitleBar.h>
#include <kddockwidgets/qtwidgets/views/TabBar.h>
#include "DebugTools/DebugInterface.h"
class DebuggerWidget;
class DockManager;
class DockViewFactory : public KDDockWidgets::QtWidgets::ViewFactory
@ -82,8 +85,17 @@ public:
DockTabBar(KDDockWidgets::Core::TabBar* controller, QWidget* parent = nullptr);
protected:
void contextMenu(QPoint pos);
bool hasDebuggerWidget(int tab_index);
void openContextMenu(QPoint pos);
struct WidgetsFromTabIndexResult
{
DebuggerWidget* debugger_widget = nullptr;
KDDockWidgets::Core::DockWidget* controller = nullptr;
KDDockWidgets::QtWidgets::DockWidget* view = nullptr;
};
void setCpuOverrideForTab(int tab_index, std::optional<BreakPointCpu> cpu_override);
WidgetsFromTabIndexResult widgetsFromTabIndex(int tab_index);
void mouseDoubleClickEvent(QMouseEvent* ev) override;
};

View File

@ -24,7 +24,7 @@ using SearchResult = MemorySearchWidget::SearchResult;
using namespace QtUtils;
MemorySearchWidget::MemorySearchWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
{
m_ui.setupUi(this);
this->repaint();

View File

@ -452,7 +452,7 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar, DebugInterface& cpu)
MemoryViewWidget
*/
MemoryViewWidget::MemoryViewWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
, m_table(this)
{
ui.setupUi(this);

View File

@ -10,7 +10,7 @@
#include <QtWidgets/QMenu>
SavedAddressesWidget::SavedAddressesWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, DISALLOW_MULTIPLE_INSTANCES)
, m_model(new SavedAddressesModel(cpu(), this))
{
m_ui.setupUi(this);

View File

@ -20,7 +20,7 @@
using namespace QtUtils;
RegisterWidget::RegisterWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
{
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);

View File

@ -9,7 +9,7 @@
#include <QtWidgets/QMenu>
StackWidget::StackWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
, m_model(new StackModel(cpu()))
{
m_ui.setupUi(this);

View File

@ -18,7 +18,7 @@ SymbolTreeWidget::SymbolTreeWidget(
u32 flags,
s32 symbol_address_alignment,
const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
, m_flags(flags)
, m_symbol_address_alignment(symbol_address_alignment)
, m_group_by_module(cpu().getCpuType() == BREAKPOINT_IOP)

View File

@ -9,7 +9,7 @@
#include <QtWidgets/QMenu>
ThreadWidget::ThreadWidget(const DebuggerWidgetParameters& parameters)
: DebuggerWidget(parameters)
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
, m_model(new ThreadModel(cpu()))
, m_proxy_model(new QSortFilterProxyModel())
{

View File

@ -33,7 +33,7 @@ enum BreakPointCpu
BREAKPOINT_IOP_AND_EE = 0x03
};
inline std::vector<BreakPointCpu> DEBUG_CPUS = {
inline const std::array<BreakPointCpu, 2> DEBUG_CPUS = {
BREAKPOINT_EE,
BREAKPOINT_IOP,
};