mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Allow having multiple dock widgets of the same type
This commit is contained in:
parent
c9ac4960bc
commit
6fe97b42c3
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
using namespace QtUtils;
|
||||
|
||||
DisassemblyWidget::DisassemblyWidget(const DebuggerWidgetParameters& parameters)
|
||||
: DebuggerWidget(parameters)
|
||||
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace DockUtils
|
|||
KDDockWidgets::QtWidgets::DockWidget* view = nullptr;
|
||||
};
|
||||
|
||||
DockWidgetPair dockWidgetFromName(QString unique_name);
|
||||
DockWidgetPair dockWidgetFromName(const QString& unique_name);
|
||||
|
||||
enum PreferredLocation
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
using namespace QtUtils;
|
||||
|
||||
RegisterWidget::RegisterWidget(const DebuggerWidgetParameters& parameters)
|
||||
: DebuggerWidget(parameters)
|
||||
: DebuggerWidget(parameters, NO_DEBUGGER_FLAGS)
|
||||
{
|
||||
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue