feat: Allow any custom content to be displayed in the command palette

This commit is contained in:
WerWolv
2025-08-15 20:17:58 +02:00
parent 0ebe4150ae
commit 762eacb7c8
9 changed files with 130 additions and 128 deletions

View File

@@ -566,7 +566,7 @@ namespace hex::plugin::builtin {
}
RequestSetPatternLanguageCode::post(patternSourceCode);
RequestRunPatternCode::post();
RequestTriggerPatternEvaluation::post();
});
}

View File

@@ -454,7 +454,7 @@ namespace hex::plugin::builtin {
continue;
result.emplace_back(name, [&toolEntry](const auto &) {
ContentRegistry::CommandPalette::setDisplayedContent([&toolEntry](const auto) {
ContentRegistry::CommandPalette::setDisplayedContent([&toolEntry]() {
toolEntry.function();
});
});

View File

@@ -75,7 +75,7 @@ namespace hex::plugin::builtin {
})
.onAppear([] {
RequestSetPatternLanguageCode::post("\n\n\n\n\n\nstruct Test {\n u8 value;\n};\n\nTest test @ 0x00;");
RequestRunPatternCode::post();
RequestTriggerPatternEvaluation::post();
})
.allowSkip();
}

View File

@@ -3,9 +3,12 @@
#include <hex/api/imhex_api/system.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/api/events/events_provider.hpp>
#include <hex/api/events/events_gui.hpp>
#include <hex/api/events/requests_gui.hpp>
#include <hex/api/events/events_interaction.hpp>
#include <hex/api/events/requests_interaction.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/utils.hpp>
@@ -24,7 +27,6 @@
#include <toasts/toast_notification.hpp>
#include <csignal>
#include <hex/api/events/events_interaction.hpp>
namespace hex::plugin::builtin {
@@ -423,8 +425,10 @@ namespace hex::plugin::builtin {
EventProviderChanged::subscribe([](auto, auto) { providerJustChanged = true; });
static prv::Provider *rightClickedProvider = nullptr;
EventSearchBoxClicked::subscribe([](ImGuiMouseButton button){
if (button == ImGuiMouseButton_Right) {
EventSearchBoxClicked::subscribe([](ImGuiMouseButton button) {
if (button == ImGuiMouseButton_Left) {
RequestOpenCommandPalette::post();
} else if (button == ImGuiMouseButton_Right) {
rightClickedProvider = ImHexApi::Provider::get();
RequestOpenPopup::post("ProviderMenu");
}

View File

@@ -2,51 +2,51 @@
#include <hex/api/content_registry/command_palette.hpp>
#include <wolv/utils/guards.hpp>
#include <hex/api/events/requests_gui.hpp>
#include <hex/api/events/events_interaction.hpp>
#include <hex/api/events/requests_interaction.hpp>
#include "imstb_textedit.h"
#include <imgui_internal.h>
#include <fonts/vscode_icons.hpp>
namespace hex::plugin::builtin {
ViewCommandPalette::ViewCommandPalette() : View::Special("hex.builtin.view.command_palette.name") {
// Add global shortcut to open the command palette
ShortcutManager::addGlobalShortcut(CTRLCMD + SHIFT + Keys::P, "hex.builtin.view.command_palette.name", [this] {
RequestOpenPopup::post("hex.builtin.view.command_palette.name"_lang);
m_commandPaletteOpen = true;
m_justOpened = true;
RequestOpenCommandPalette::post();
});
EventSearchBoxClicked::subscribe([this](ImGuiMouseButton button) {
if (button == ImGuiMouseButton_Left) {
m_commandPaletteOpen = true;
m_justOpened = true;
}
RequestOpenCommandPalette::subscribe([this]() {
m_commandPaletteOpen = true;
m_justOpened = true;
ContentRegistry::CommandPalette::impl::getDisplayedContent().reset();
});
}
void ViewCommandPalette::drawAlwaysVisibleContent() {
auto &displayedContent = ContentRegistry::CommandPalette::impl::getDisplayedContent();
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 (!m_commandPaletteOpen) return;
auto windowPos = ImHexApi::System::getMainWindowPosition();
auto windowSize = ImHexApi::System::getMainWindowSize();
const auto &displayedContent = ContentRegistry::CommandPalette::impl::getDisplayedContent();
const auto windowPos = ImHexApi::System::getMainWindowPosition();
const auto windowSize = ImHexApi::System::getMainWindowSize();
ImGui::SetNextWindowPos(ImVec2(windowPos.x + windowSize.x * 0.5F, windowPos.y), ImGuiCond_Always, ImVec2(0.5F, 0.0F));
if (!displayedContent.has_value())
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
else
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), ImVec2(FLT_MAX, FLT_MAX));
ImGui::SetNextWindowSizeConstraints(ImVec2(this->getMinSize().x, 20_scaled), ImVec2(FLT_MAX, FLT_MAX));
if (ImGui::BeginPopup("hex.builtin.view.command_palette.name"_lang)) {
const bool hasSearchBox = !displayedContent.has_value() || displayedContent->showSearchBox;
if (ImGui::BeginPopup("hex.builtin.view.command_palette.name"_lang, !hasSearchBox ? ImGuiWindowFlags_MenuBar : ImGuiWindowFlags_None)) {
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
ImGui::BringWindowToFocusFront(ImGui::GetCurrentWindowRead());
@@ -54,82 +54,92 @@ namespace hex::plugin::builtin {
if (ImGui::IsKeyDown(ImGuiKey_Escape))
ImGui::CloseCurrentPopup();
if (hasSearchBox) {
const auto buttonColor = [](float alpha) {
return ImU32(ImColor(ImGui::GetStyleColorVec4(ImGuiCol_ModalWindowDimBg) * ImVec4(1, 1, 1, alpha)));
};
const auto buttonColor = [](float alpha) {
return ImU32(ImColor(ImGui::GetStyleColorVec4(ImGuiCol_ModalWindowDimBg) * ImVec4(1, 1, 1, alpha)));
};
// Draw the main input text box
ImGui::PushItemWidth(-1);
ImGui::PushStyleColor(ImGuiCol_FrameBg, buttonColor(0.5F));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, buttonColor(0.7F));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, buttonColor(0.9F));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0_scaled);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4_scaled);
// Draw the main input text box
ImGui::PushItemWidth(-1);
ImGui::PushStyleColor(ImGuiCol_FrameBg, buttonColor(0.5F));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, buttonColor(0.7F));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, buttonColor(0.9F));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0_scaled);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4_scaled);
if (ImGui::InputText("##command_input", m_commandBuffer)) {
m_lastResults = this->getCommandResults(m_commandBuffer);
}
ImGui::SetItemKeyOwner(ImGuiKey_LeftAlt, ImGuiInputFlags_CondActive);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
ImGui::PopItemWidth();
ImGui::SetItemDefaultFocus();
if (m_moveCursorToEnd) {
auto textState = ImGui::GetInputTextState(ImGui::GetID("##command_input"));
if (textState != nullptr) {
auto stb = reinterpret_cast<STB_TexteditState*>(textState->Stb);
stb->cursor =
stb->select_start =
stb->select_end = m_commandBuffer.size();
if (ImGui::InputText("##command_input", m_commandBuffer)) {
m_lastResults = this->getCommandResults(m_commandBuffer);
}
m_moveCursorToEnd = false;
}
ImGui::SetItemKeyOwner(ImGuiKey_LeftAlt, ImGuiInputFlags_CondActive);
// Handle giving back focus to the input text box
if (m_focusInputTextBox) {
ImGui::SetKeyboardFocusHere(-1);
ImGui::ActivateItemByID(ImGui::GetID("##command_input"));
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
ImGui::PopItemWidth();
ImGui::SetItemDefaultFocus();
m_focusInputTextBox = false;
m_moveCursorToEnd = true;
}
if (m_moveCursorToEnd) {
auto textState = ImGui::GetInputTextState(ImGui::GetID("##command_input"));
if (textState != nullptr) {
auto stb = reinterpret_cast<STB_TexteditState*>(textState->Stb);
stb->cursor =
stb->select_start =
stb->select_end = m_commandBuffer.size();
}
m_moveCursorToEnd = false;
}
// Execute the currently selected command when pressing enter
if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false))) {
bool closePalette = true;
if (!m_lastResults.empty()) {
auto &[displayResult, matchedCommand, callback] = m_lastResults.front();
// Handle giving back focus to the input text box
if (m_focusInputTextBox) {
ImGui::SetKeyboardFocusHere(-1);
ImGui::ActivateItemByID(ImGui::GetID("##command_input"));
if (auto result = callback(matchedCommand); result.has_value()) {
m_commandBuffer = result.value();
closePalette = false;
m_focusInputTextBox = true;
m_focusInputTextBox = false;
m_moveCursorToEnd = true;
}
// Execute the currently selected command when pressing enter
if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter, false) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter, false))) {
bool closePalette = true;
if (!m_lastResults.empty()) {
auto &[displayResult, matchedCommand, callback] = m_lastResults.front();
if (auto result = callback(matchedCommand); result.has_value()) {
m_commandBuffer = result.value();
closePalette = false;
m_focusInputTextBox = true;
}
}
if (closePalette) {
ImGui::CloseCurrentPopup();
}
}
if (closePalette) {
ImGui::CloseCurrentPopup();
// Focus the input text box when the popup is opened
if (m_justOpened) {
focusInputTextBox();
m_lastResults = this->getCommandResults("");
m_commandBuffer.clear();
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y);
ImGui::Separator();
}
// Focus the input text box when the popup is opened
if (m_justOpened) {
focusInputTextBox();
m_lastResults = this->getCommandResults("");
m_commandBuffer.clear();
m_justOpened = false;
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y);
ImGui::Separator();
m_justOpened = false;
// Draw the results
if (displayedContent.has_value()) {
(*displayedContent)(m_commandBuffer);
if (!displayedContent->showSearchBox){
if (ImGui::BeginMenuBar()) {
ImGui::TextUnformatted(ICON_VS_TARGET);
ImGui::EndMenuBar();
}
displayedContent->callback();
}
} else {
if (ImGui::BeginChild("##results", ImGui::GetContentRegionAvail(), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
u32 id = 1;
@@ -252,4 +262,4 @@ namespace hex::plugin::builtin {
return results;
}
}
}

View File

@@ -238,7 +238,7 @@ namespace hex::plugin::builtin {
ViewPatternEditor::~ViewPatternEditor() {
RequestPatternEditorSelectionChange::unsubscribe(this);
RequestSetPatternLanguageCode::unsubscribe(this);
RequestRunPatternCode::unsubscribe(this);
RequestTriggerPatternEvaluation::unsubscribe(this);
EventFileLoaded::unsubscribe(this);
EventProviderChanged::unsubscribe(this);
EventProviderClosed::unsubscribe(this);
@@ -1854,20 +1854,10 @@ namespace hex::plugin::builtin {
m_textEditor.get(provider).setCursorPosition(coords);
});
RequestLoadPatternLanguageFile::subscribe(this, [this](const std::fs::path &path, bool trackFile) {
this->loadPatternFile(path, ImHexApi::Provider::get(), trackFile);
});
RequestRunPatternCode::subscribe(this, [this] {
RequestTriggerPatternEvaluation::subscribe(this, [this] {
m_triggerAutoEvaluate = true;
});
RequestSavePatternLanguageFile::subscribe(this, [this](const std::fs::path &path) {
auto provider = ImHexApi::Provider::get();
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.get(provider).getText()));
});
RequestSetPatternLanguageCode::subscribe(this, [this](const std::string &code) {
auto provider = ImHexApi::Provider::get();
m_textEditor.get(provider).setText(wolv::util::preprocessText(code));