feat: Allow custom UIs to be drawn in the command palette

This commit is contained in:
WerWolv
2025-08-15 17:29:00 +02:00
parent 5853cb5ad5
commit 3ed7834945
3 changed files with 55 additions and 23 deletions

View File

@@ -26,6 +26,7 @@ EXPORT_MODULE namespace hex {
std::function<void(std::string)> callback; std::function<void(std::string)> callback;
}; };
using ContentDisplayCallback = std::function<void(std::string)>;
using DisplayCallback = std::function<std::string(std::string)>; using DisplayCallback = std::function<std::string(std::string)>;
using ExecuteCallback = std::function<std::optional<std::string>(std::string)>; using ExecuteCallback = std::function<std::optional<std::string>(std::string)>;
using QueryCallback = std::function<std::vector<QueryResult>(std::string)>; using QueryCallback = std::function<std::vector<QueryResult>(std::string)>;
@@ -48,6 +49,8 @@ EXPORT_MODULE namespace hex {
const std::vector<Entry>& getEntries(); const std::vector<Entry>& getEntries();
const std::vector<Handler>& getHandlers(); const std::vector<Handler>& getHandlers();
std::optional<ContentDisplayCallback>& getDisplayedContent();
} }
/** /**
@@ -77,6 +80,12 @@ EXPORT_MODULE namespace hex {
const std::string &command, const std::string &command,
const impl::QueryCallback &queryCallback, const impl::QueryCallback &queryCallback,
const impl::DisplayCallback &displayCallback); const impl::DisplayCallback &displayCallback);
/**
* @brief Specify UI content that will be displayed inside the command palette
* @param displayCallback Display callback that will be called to display the content
*/
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback);
} }
} }

View File

@@ -613,6 +613,11 @@ namespace hex {
return *s_handlers; return *s_handlers;
} }
static AutoReset<std::optional<ContentDisplayCallback>> s_displayedContent;
std::optional<ContentDisplayCallback>& getDisplayedContent() {
return *s_displayedContent;
}
} }
void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) { void add(Type type, const std::string &command, const UnlocalizedString &unlocalizedDescription, const impl::DisplayCallback &displayCallback, const impl::ExecuteCallback &executeCallback) {
@@ -627,6 +632,10 @@ namespace hex {
impl::s_handlers->push_back(impl::Handler { type, command, queryCallback, displayCallback }); impl::s_handlers->push_back(impl::Handler { type, command, queryCallback, displayCallback });
} }
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) {
impl::s_displayedContent = displayCallback;
}
} }

View File

@@ -20,7 +20,6 @@ namespace hex::plugin::builtin {
EventSearchBoxClicked::subscribe([this](ImGuiMouseButton button) { EventSearchBoxClicked::subscribe([this](ImGuiMouseButton button) {
if (button == ImGuiMouseButton_Left) { if (button == ImGuiMouseButton_Left) {
RequestOpenPopup::post("hex.builtin.view.command_palette.name"_lang);
m_commandPaletteOpen = true; m_commandPaletteOpen = true;
m_justOpened = true; m_justOpened = true;
} }
@@ -28,14 +27,25 @@ namespace hex::plugin::builtin {
} }
void ViewCommandPalette::drawAlwaysVisibleContent() { void ViewCommandPalette::drawAlwaysVisibleContent() {
if (m_justOpened) {
ImGui::OpenPopup("hex.builtin.view.command_palette.name"_lang);
ContentRegistry::CommandPalette::impl::getDisplayedContent().reset();
}
// If the command palette is hidden, don't draw it // If the command palette is hidden, don't draw it
if (!m_commandPaletteOpen) return; if (!m_commandPaletteOpen) return;
auto windowPos = ImHexApi::System::getMainWindowPosition(); auto windowPos = ImHexApi::System::getMainWindowPosition();
auto windowSize = ImHexApi::System::getMainWindowSize(); auto windowSize = ImHexApi::System::getMainWindowSize();
const auto &displayedContent = ContentRegistry::CommandPalette::impl::getDisplayedContent();
ImGui::SetNextWindowPos(ImVec2(windowPos.x + windowSize.x * 0.5F, windowPos.y), ImGuiCond_Always, ImVec2(0.5F, 0.0F)); ImGui::SetNextWindowPos(ImVec2(windowPos.x + windowSize.x * 0.5F, windowPos.y), ImGuiCond_Always, ImVec2(0.5F, 0.0F));
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize()); if (!displayedContent.has_value())
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
else
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), ImVec2(FLT_MAX, FLT_MAX));
if (ImGui::BeginPopup("hex.builtin.view.command_palette.name"_lang)) { if (ImGui::BeginPopup("hex.builtin.view.command_palette.name"_lang)) {
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead()); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
ImGui::BringWindowToFocusFront(ImGui::GetCurrentWindowRead()); ImGui::BringWindowToFocusFront(ImGui::GetCurrentWindowRead());
@@ -118,33 +128,37 @@ namespace hex::plugin::builtin {
ImGui::Separator(); ImGui::Separator();
// Draw the results // Draw the results
if (ImGui::BeginChild("##results", ImGui::GetContentRegionAvail(), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { if (displayedContent.has_value()) {
u32 id = 1; (*displayedContent)(m_commandBuffer);
for (const auto &[displayResult, matchedCommand, callback] : m_lastResults) { } else {
ImGui::PushID(id); if (ImGui::BeginChild("##results", ImGui::GetContentRegionAvail(), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, false); u32 id = 1;
ON_SCOPE_EXIT { for (const auto &[displayResult, matchedCommand, callback] : m_lastResults) {
ImGui::PopItemFlag(); ImGui::PushID(id);
ImGui::PopID(); ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, false);
id += 1; ON_SCOPE_EXIT {
}; ImGui::PopItemFlag();
ImGui::PopID();
id += 1;
};
// Allow executing a command by clicking on it or selecting it with the keyboard and pressing enter // Allow executing a command by clicking on it or selecting it with the keyboard and pressing enter
if (ImGui::Selectable(displayResult.c_str(), false, ImGuiSelectableFlags_NoAutoClosePopups)) { if (ImGui::Selectable(displayResult.c_str(), false, ImGuiSelectableFlags_NoAutoClosePopups)) {
if (auto result = callback(matchedCommand); result.has_value()) if (auto result = callback(matchedCommand); result.has_value())
m_commandBuffer = result.value(); m_commandBuffer = result.value();
break; break;
} }
if (ImGui::IsItemFocused() && (ImGui::IsKeyDown(ImGuiKey_Enter) || ImGui::IsKeyDown(ImGuiKey_KeypadEnter))) { if (ImGui::IsItemFocused() && (ImGui::IsKeyDown(ImGuiKey_Enter) || ImGui::IsKeyDown(ImGuiKey_KeypadEnter))) {
if (auto result = callback(matchedCommand); result.has_value()) if (auto result = callback(matchedCommand); result.has_value())
m_commandBuffer = result.value(); m_commandBuffer = result.value();
break; break;
}
} }
} }
ImGui::EndChild();
} }
ImGui::EndChild();
ImGui::EndPopup(); ImGui::EndPopup();
} else { } else {