From 9c98b659f1b6f4727a3f9b0b85da329923bc4dfd Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Wed, 26 Feb 2020 21:33:43 +0100 Subject: [PATCH] Qt/CodeViewWidget: Indent branch arrows based on free space rather than reserving a full column for each. --- .../DolphinQt/Debugger/CodeViewWidget.cpp | 81 +++++++++++++++++-- .../Core/DolphinQt/Debugger/CodeViewWidget.h | 2 + 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp index 0ff0f4c8ae..b9cff5549a 100644 --- a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp @@ -36,6 +36,7 @@ struct CodeViewBranch { u32 src_addr; u32 dst_addr; + u32 indentation = 0; bool is_link; }; @@ -60,11 +61,10 @@ private: constexpr u32 x_offset_in_branch_for_vertical_line = 10; const u32 addr = m_parent->AddressForRow(index.row()); - u32 current_branch_index = 0; for (const CodeViewBranch& branch : m_parent->m_branches) { const int y_center = option.rect.top() + option.rect.height() / 2; - const int x_left = option.rect.left() + WIDTH_PER_BRANCH_ARROW * current_branch_index; + const int x_left = option.rect.left() + WIDTH_PER_BRANCH_ARROW * branch.indentation; const int x_right = x_left + x_offset_in_branch_for_vertical_line; if (branch.is_link) @@ -108,8 +108,6 @@ private: } } } - - ++current_branch_index; } painter->restore(); @@ -322,15 +320,88 @@ void CodeViewWidget::Update() } } + CalculateBranchIndentation(); + + u32 max_indent = 0; + for (const CodeViewBranch& branch : m_branches) + max_indent = std::max(max_indent, branch.indentation); + resizeColumnsToContents(); setColumnWidth(CODE_VIEW_COLUMN_BRANCH_ARROWS, - static_cast(WIDTH_PER_BRANCH_ARROW * m_branches.size())); + static_cast(WIDTH_PER_BRANCH_ARROW * (max_indent + 1))); g_symbolDB.FillInCallers(); repaint(); m_updating = false; } +void CodeViewWidget::CalculateBranchIndentation() +{ + const size_t rows = rowCount(); + const size_t columns = m_branches.size(); + if (rows < 1 || columns < 1) + return; + + // process in order of how much vertical space the drawn arrow would take up + // so shorter arrows go further to the left + const auto priority = [](const CodeViewBranch& b) { + return b.is_link ? 0 : (std::max(b.src_addr, b.dst_addr) - std::min(b.src_addr, b.dst_addr)); + }; + std::stable_sort(m_branches.begin(), m_branches.end(), + [&priority](const CodeViewBranch& lhs, const CodeViewBranch& rhs) { + return priority(lhs) < priority(rhs); + }); + + // build a 2D lookup table representing the columns and rows the arrow could be drawn in + // and try to place all branch arrows in it as far left as possible + std::vector arrow_space_used(columns * rows, false); + const auto index = [&](u32 column, u32 row) { return column * rows + row; }; + const u32 first_visible_addr = AddressForRow(0); + const u32 last_visible_addr = AddressForRow(static_cast(rows - 1)); + for (CodeViewBranch& branch : m_branches) + { + const u32 arrow_src_addr = branch.src_addr; + const u32 arrow_dst_addr = branch.is_link ? branch.src_addr : branch.dst_addr; + const u32 arrow_addr_lower = std::min(arrow_src_addr, arrow_dst_addr); + const u32 arrow_addr_higher = std::max(arrow_src_addr, arrow_dst_addr); + const bool is_visible = + last_visible_addr >= arrow_addr_lower || first_visible_addr <= arrow_addr_higher; + if (!is_visible) + continue; + + const u32 arrow_first_visible_addr = + std::clamp(arrow_addr_lower, first_visible_addr, last_visible_addr); + const u32 arrow_last_visible_addr = + std::clamp(arrow_addr_higher, first_visible_addr, last_visible_addr); + const u32 arrow_first_visible_row = (arrow_first_visible_addr - first_visible_addr) / 4; + const u32 arrow_last_visible_row = (arrow_last_visible_addr - first_visible_addr) / 4; + + const auto free_column = [&]() -> std::optional { + for (u32 column = 0; column < columns; ++column) + { + const bool column_is_free = [&] { + for (u32 row = arrow_first_visible_row; row <= arrow_last_visible_row; ++row) + { + if (arrow_space_used[index(column, row)]) + return false; + } + return true; + }(); + if (column_is_free) + return column; + } + return std::nullopt; + }(); + + if (!free_column) + continue; + + branch.indentation = *free_column; + for (u32 row = arrow_first_visible_row; row <= arrow_last_visible_row; ++row) + arrow_space_used[index(*free_column, row)] = true; + } +} + u32 CodeViewWidget::GetAddress() const { return m_address; diff --git a/Source/Core/DolphinQt/Debugger/CodeViewWidget.h b/Source/Core/DolphinQt/Debugger/CodeViewWidget.h index 917948f8e6..eddb3b09f7 100644 --- a/Source/Core/DolphinQt/Debugger/CodeViewWidget.h +++ b/Source/Core/DolphinQt/Debugger/CodeViewWidget.h @@ -85,6 +85,8 @@ private: void OnReplaceInstruction(); void OnRestoreInstruction(); + void CalculateBranchIndentation(); + bool m_updating = false; u32 m_address = 0;