mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-27 23:37:05 -05:00
feat: Allow any custom content to be displayed in the command palette
This commit is contained in:
@@ -21,12 +21,14 @@ EXPORT_MODULE namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
using QueryResultCallback = std::function<void(std::string)>;
|
||||
|
||||
struct QueryResult {
|
||||
std::string name;
|
||||
std::function<void(std::string)> callback;
|
||||
QueryResultCallback callback;
|
||||
};
|
||||
|
||||
using ContentDisplayCallback = std::function<void(std::string)>;
|
||||
using ContentDisplayCallback = std::function<void()>;
|
||||
using DisplayCallback = std::function<std::string(std::string)>;
|
||||
using ExecuteCallback = std::function<std::optional<std::string>(std::string)>;
|
||||
using QueryCallback = std::function<std::vector<QueryResult>(std::string)>;
|
||||
@@ -46,10 +48,15 @@ EXPORT_MODULE namespace hex {
|
||||
DisplayCallback displayCallback;
|
||||
};
|
||||
|
||||
struct ContentDisplay {
|
||||
bool showSearchBox;
|
||||
ContentDisplayCallback callback;
|
||||
};
|
||||
|
||||
const std::vector<Entry>& getEntries();
|
||||
const std::vector<Handler>& getHandlers();
|
||||
|
||||
std::optional<ContentDisplayCallback>& getDisplayedContent();
|
||||
std::optional<ContentDisplay>& getDisplayedContent();
|
||||
|
||||
}
|
||||
|
||||
@@ -86,6 +93,12 @@ EXPORT_MODULE namespace hex {
|
||||
* @param displayCallback Display callback that will be called to display the content
|
||||
*/
|
||||
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback);
|
||||
|
||||
/**
|
||||
* @brief Opens the command palette window, displaying a user defined interface
|
||||
* @param displayCallback Display callback that will be called to display the content
|
||||
*/
|
||||
void openWithContent(const impl::ContentDisplayCallback &displayCallback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,34 +71,8 @@ namespace hex {
|
||||
/**
|
||||
* @brief Requests the Pattern editor to run the current code
|
||||
*
|
||||
* This is only ever used in the introduction tutorial.
|
||||
*
|
||||
* FIXME: the name is misleading, as for now this activates the pattern's auto-evaluation rather than a
|
||||
* one-off execution
|
||||
*/
|
||||
EVENT_DEF(RequestRunPatternCode);
|
||||
|
||||
/**
|
||||
* @brief Request to load a pattern language file
|
||||
*
|
||||
* FIXME: this request is unused, as now another component is responsible for pattern file loading.
|
||||
* This request should be scrapped.
|
||||
*
|
||||
* @param path the pattern file's path
|
||||
* @param bool track changes to the file on disk
|
||||
*
|
||||
*/
|
||||
EVENT_DEF(RequestLoadPatternLanguageFile, std::fs::path, bool);
|
||||
|
||||
/**
|
||||
* @brief Request to save a pattern language file
|
||||
*
|
||||
* FIXME: this request is unused, as now another component is responsible for pattern file saving.
|
||||
* This request should be scrapped.
|
||||
*
|
||||
* @param path the pattern file's path
|
||||
*/
|
||||
EVENT_DEF(RequestSavePatternLanguageFile, std::fs::path);
|
||||
EVENT_DEF(RequestTriggerPatternEvaluation);
|
||||
|
||||
/**
|
||||
* @brief Requests ImHex to open and process a file
|
||||
@@ -116,4 +90,9 @@ namespace hex {
|
||||
*/
|
||||
EVENT_DEF(RequestAddVirtualFile, std::fs::path, std::vector<u8>, Region);
|
||||
|
||||
/**
|
||||
* @brief Requests the command palette to be opened
|
||||
*/
|
||||
EVENT_DEF(RequestOpenCommandPalette);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <jthread.hpp>
|
||||
#include <hex/api/events/requests_interaction.hpp>
|
||||
|
||||
#if defined(OS_WEB)
|
||||
#include <emscripten.h>
|
||||
@@ -613,8 +614,8 @@ namespace hex {
|
||||
return *s_handlers;
|
||||
}
|
||||
|
||||
static AutoReset<std::optional<ContentDisplayCallback>> s_displayedContent;
|
||||
std::optional<ContentDisplayCallback>& getDisplayedContent() {
|
||||
static AutoReset<std::optional<ContentDisplay>> s_displayedContent;
|
||||
std::optional<ContentDisplay>& getDisplayedContent() {
|
||||
return *s_displayedContent;
|
||||
}
|
||||
|
||||
@@ -633,7 +634,12 @@ namespace hex {
|
||||
}
|
||||
|
||||
void setDisplayedContent(const impl::ContentDisplayCallback &displayCallback) {
|
||||
impl::s_displayedContent = displayCallback;
|
||||
impl::s_displayedContent = impl::ContentDisplay { true, displayCallback };
|
||||
}
|
||||
|
||||
void openWithContent(const impl::ContentDisplayCallback &displayCallback) {
|
||||
RequestOpenCommandPalette::post();
|
||||
impl::s_displayedContent = impl::ContentDisplay { false, displayCallback };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -566,7 +566,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
RequestSetPatternLanguageCode::post(patternSourceCode);
|
||||
RequestRunPatternCode::post();
|
||||
RequestTriggerPatternEvaluation::post();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user