feat: Add keyboard shortcuts to jump to the prev/next differences in diff view (#2445)

Adds keyboard shortcuts (currently `n` and `N`) in the diffing plugin
view to jump to the next/prev difference in the list.

IMPORTANT NOTE: Depends on changes made in a library submodule. [This
PR](https://github.com/WerWolv/libwolv/pull/34) must be accepted first.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Zackary Newman 2025-09-21 04:39:37 -04:00 committed by GitHub
parent fedbce6f9e
commit 3c167c3306
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 121 additions and 7 deletions

@ -1 +1 @@
Subproject commit 56f77945fe292a4d00afdb1961f2b7e87a88be4c
Subproject commit 52cb24b1842cfc3e9cc681ba67090b0327efb2a3

View File

@ -1345,7 +1345,8 @@ namespace hex::plugin::builtin {
/* Skip until */
ContentRegistry::UserInterface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.builtin.view.hex_editor.menu.file.skip_until" }, ICON_VS_DEBUG_STEP_OVER, 1610,
[]{},
canSearchForDifferingByte);
canSearchForDifferingByte,
this);
/* Skip until previous differing byte */
ContentRegistry::UserInterface::addMenuItem({
@ -1384,7 +1385,8 @@ namespace hex::plugin::builtin {
ui::ToastInfo::open("hex.builtin.view.hex_editor.menu.file.skip_until.beginning_reached"_lang);
}
},
canSearchForDifferingByte);
canSearchForDifferingByte,
this);
/* Skip until next differing byte */
ContentRegistry::UserInterface::addMenuItem({
@ -1423,7 +1425,8 @@ namespace hex::plugin::builtin {
ui::ToastInfo::open("hex.builtin.view.hex_editor.menu.file.skip_until.end_reached"_lang);
}
},
canSearchForDifferingByte);
canSearchForDifferingByte,
this);
ContentRegistry::UserInterface::addMenuItemSeparator({ "hex.builtin.menu.edit" }, 1100, this);

View File

@ -36,6 +36,8 @@ namespace hex::plugin::diffing {
std::function<std::optional<color_t>(u64, const u8*, size_t)> createCompareFunction(size_t otherIndex) const;
void analyze(prv::Provider *providerA, prv::Provider *providerB);
void registerMenuItems();
void reset();
private:
@ -45,6 +47,9 @@ namespace hex::plugin::diffing {
std::atomic<bool> m_analyzed = false;
std::atomic<bool> m_analysisInterrupted = false;
ContentRegistry::Diffing::Algorithm *m_algorithm = nullptr;
u64 m_selectedAddress = 0;
prv::Provider *m_selectedProvider = nullptr;
};
}
}

View File

@ -14,5 +14,10 @@
"hex.diffing.view.diff.algorithm": "Diffing Algorithm",
"hex.diffing.view.diff.settings": "No settings available",
"hex.diffing.view.diff.settings.no_settings": "No settings available",
"hex.diffing.view.diff.task.diffing": "Diffing data..."
}
"hex.diffing.view.diff.task.diffing": "Diffing data...",
"hex.diffing.view.diff.menu.file.jumping": "Jump Between Differences",
"hex.diffing.view.diff.menu.file.jumping.prev_diff": "Jump to Previous Difference",
"hex.diffing.view.diff.menu.file.jumping.next_diff": "Jump to Next Difference",
"hex.diffing.view.diff.jumping.beginning_reached": "No more differences till the beginning of the file",
"hex.diffing.view.diff.jumping.end_reached": "No more differences till the end of the file"
}

View File

@ -1,12 +1,15 @@
#include "content/views/view_diff.hpp"
#include <toasts/toast_notification.hpp>
#include <hex/api/imhex_api/provider.hpp>
#include <hex/api/events/requests_gui.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <fonts/vscode_icons.hpp>
#include <fonts/tabler_icons.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::diffing {
@ -22,14 +25,28 @@ namespace hex::plugin::diffing {
m_analysisInterrupted = m_analyzed = false;
});
// Handle region selection
EventRegionSelected::subscribe(this, [this](const auto &region) {
// Save current selection
if (!ImHexApi::Provider::isValid() || region == Region::Invalid()) {
m_selectedProvider = nullptr;
} else {
m_selectedAddress = region.address;
m_selectedProvider = region.getProvider();
}
});
// Set the background highlight callbacks for the two hex editor columns
m_columns[0].hexEditor.setBackgroundHighlightCallback(this->createCompareFunction(1));
m_columns[1].hexEditor.setBackgroundHighlightCallback(this->createCompareFunction(0));
this->registerMenuItems();
}
ViewDiff::~ViewDiff() {
EventProviderClosed::unsubscribe(this);
EventDataChanged::unsubscribe(this);
EventRegionSelected::unsubscribe(this);
}
namespace {
@ -133,6 +150,7 @@ namespace hex::plugin::diffing {
column.diffTree.clear();
column.differences.clear();
}
m_analysisInterrupted = m_analyzed = false;
}
@ -441,5 +459,88 @@ namespace hex::plugin::diffing {
}
}
void ViewDiff::registerMenuItems() {
ContentRegistry::UserInterface::addMenuItemSeparator({ "hex.builtin.menu.file" }, 1700, this);
ContentRegistry::UserInterface::addMenuItemSubMenu({ "hex.builtin.menu.file", "hex.diffing.view.diff.menu.file.jumping" }, ICON_TA_ARROWS_MOVE_HORIZONTAL, 1710,
[]{},
[this]{ return (bool) m_analyzed; },
this);
ContentRegistry::UserInterface::addMenuItem({
"hex.builtin.menu.file",
"hex.diffing.view.diff.menu.file.jumping",
"hex.diffing.view.diff.menu.file.jumping.prev_diff"
},
ICON_TA_ARROW_BAR_TO_LEFT_DASHED,
1720,
CTRLCMD + Keys::Left,
[this] {
if (m_selectedProvider == nullptr)
return;
// Get the column of the currently selected region
auto providers = ImHexApi::Provider::getProviders();
Column *selectedColumn = nullptr;
for (auto &column : m_columns) {
if (providers[column.provider] == m_selectedProvider) {
selectedColumn = &column;
break;
}
}
if (selectedColumn == nullptr)
return;
// Jump to previous difference
auto prevRange = selectedColumn->diffTree.prevInterval(m_selectedAddress);
if (prevRange.has_value()) {
selectedColumn->hexEditor.setSelection(prevRange->interval.start, prevRange->interval.end);
selectedColumn->hexEditor.jumpToSelection();
} else {
ui::ToastInfo::open("hex.diffing.view.diff.jumping.beginning_reached"_lang);
}
},
[this]{ return (bool) m_analyzed; },
this
);
ContentRegistry::UserInterface::addMenuItem({
"hex.builtin.menu.file",
"hex.diffing.view.diff.menu.file.jumping",
"hex.diffing.view.diff.menu.file.jumping.next_diff"
},
ICON_TA_ARROW_BAR_TO_RIGHT_DASHED,
1730,
CTRLCMD + Keys::Right,
[this] {
if (m_selectedProvider == nullptr)
return;
// Get the column of the currently selected region
auto providers = ImHexApi::Provider::getProviders();
Column *selectedColumn = nullptr;
for (auto &column : m_columns) {
if (providers[column.provider] == m_selectedProvider) {
selectedColumn = &column;
break;
}
}
if (selectedColumn == nullptr)
return;
// Jump to next difference
auto nextRange = selectedColumn->diffTree.nextInterval(m_selectedAddress);
if (nextRange.has_value()) {
selectedColumn->hexEditor.setSelection(nextRange->interval.start, nextRange->interval.end);
selectedColumn->hexEditor.jumpToSelection();
} else {
ui::ToastInfo::open("hex.diffing.view.diff.jumping.end_reached"_lang);
}
},
[this]{ return (bool) m_analyzed; },
this
);
}
}