aboutsummaryrefslogtreecommitdiff
path: root/source/30-game/EditorCommandPalette.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2023-10-19 22:50:07 -0700
committerrtk0c <[email protected]>2023-10-19 22:50:07 -0700
commit2c92e07f337e42cf58970443f9de678f85a9b2a4 (patch)
tree075d5407e1e12a9d35cbee6e4c20ad34e0765c42 /source/30-game/EditorCommandPalette.cpp
parent615809c036f604bce4582cea8ad49c64693f4f45 (diff)
The great renaming: switch to "module style"
Diffstat (limited to 'source/30-game/EditorCommandPalette.cpp')
-rw-r--r--source/30-game/EditorCommandPalette.cpp406
1 files changed, 0 insertions, 406 deletions
diff --git a/source/30-game/EditorCommandPalette.cpp b/source/30-game/EditorCommandPalette.cpp
deleted file mode 100644
index 0e7b894..0000000
--- a/source/30-game/EditorCommandPalette.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-#include "EditorCommandPalette.hpp"
-
-#include "AppConfig.hpp"
-#include "EditorUtils.hpp"
-#include "FuzzyMatch.hpp"
-#include "Utils.hpp"
-
-#include <GLFW/glfw3.h>
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include <imgui_internal.h>
-
-using namespace std::literals;
-
-bool EditorCommandExecuteContext::IsInitiated() const {
- return mCommand != nullptr;
-}
-
-const EditorCommand* EditorCommandExecuteContext::GetCurrentCommand() const {
- return mCommand;
-}
-
-void EditorCommandExecuteContext::Initiate(const EditorCommand& command) {
- if (mCommand == nullptr) {
- mCommand = &command;
- }
-}
-
-void EditorCommandExecuteContext::Prompt(std::vector<std::string> options) {
- assert(mCommand != nullptr);
- mCurrentOptions = std::move(options);
- ++mDepth;
-}
-
-void EditorCommandExecuteContext::Finish() {
- assert(mCommand != nullptr);
- mCommand = nullptr;
- mCurrentOptions.clear();
- mDepth = 0;
-}
-
-int EditorCommandExecuteContext::GetExecutionDepth() const {
- return mDepth;
-}
-
-struct EditorCommandPalette::SearchResult {
- int itemIndex;
- int score;
- int matchCount;
- uint8_t matches[32];
-};
-
-struct EditorCommandPalette::Item {
- bool hovered = false;
- bool held = false;
-};
-
-EditorCommandPalette::EditorCommandPalette() = default;
-EditorCommandPalette::~EditorCommandPalette() = default;
-
-namespace P6503_UNITY_ID {
-std::string MakeCommandName(std::string_view category, std::string_view name) {
- std::string result;
- constexpr auto infix = ": "sv;
- result.reserve(category.size() + infix.size() + name.size());
- result.append(category);
- result.append(infix);
- result.append(name);
- return result;
-}
-} // namespace P6503_UNITY_ID
-
-void EditorCommandPalette::AddCommand(std::string_view category, std::string_view name, EditorCommand command) {
- command.name = P6503_UNITY_ID::MakeCommandName(category, name);
-
- auto location = std::lower_bound(
- mCommands.begin(),
- mCommands.end(),
- command,
- [](const EditorCommand& a, const EditorCommand& b) -> bool {
- return a.name < b.name;
- });
- auto iter = mCommands.insert(location, std::move(command));
-
- InvalidateSearchResults();
-}
-
-void EditorCommandPalette::RemoveCommand(std::string_view category, std::string_view name) {
- auto commandName = P6503_UNITY_ID::MakeCommandName(category, name);
- RemoveCommand(commandName);
-}
-
-void EditorCommandPalette::RemoveCommand(const std::string& commandName) {
- struct Comparator {
- bool operator()(const EditorCommand& command, const std::string& str) const {
- return command.name < str;
- }
-
- bool operator()(const std::string& str, const EditorCommand& command) const {
- return str < command.name;
- }
- };
-
- auto range = std::equal_range(mCommands.begin(), mCommands.end(), commandName, Comparator{});
- mCommands.erase(range.first, range.second);
-
- InvalidateSearchResults();
-}
-
-void EditorCommandPalette::Show(bool* open) {
- // Center window horizontally, align top vertically
- ImGui::SetNextWindowPos(ImVec2(ImGui::GetMainViewport()->Size.x / 2, 0), ImGuiCond_Always, ImVec2(0.5f, 0.0f));
- ImGui::SetNextWindowSizeRelScreen(0.3f, 0.0f);
-
- ImGui::Begin("Command Palette", open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar);
- float width = ImGui::GetWindowContentRegionMax().x - ImGui::GetWindowContentRegionMin().x;
-
- if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows) || mShouldCloseNextFrame) {
- // Close popup when user unfocused the command palette window (clicking elsewhere)
- // or some action requested closing this window
- mShouldCloseNextFrame = false;
- if (open) {
- *open = false;
- }
- }
-
- if (ImGui::IsWindowAppearing() || mFocusSearchBox) {
- mFocusSearchBox = false;
-
- // Focus the search box when user first brings command palette window up
- // Note: this only affects the next frame
- ImGui::SetKeyboardFocusHere(0);
- }
- ImGui::SetNextItemWidth(width);
- if (ImGui::InputText("##", &mSearchText)) {
- // Search string updated, update search results
-
- mFocusedItemId = 0;
- mSearchResults.clear();
-
- size_t itemCount;
- if (mExeCtx.GetExecutionDepth() == 0) {
- itemCount = mCommands.size();
- } else {
- itemCount = mExeCtx.mCurrentOptions.size();
- }
-
- for (size_t i = 0; i < itemCount; ++i) {
- const char* text;
- if (mExeCtx.GetExecutionDepth() == 0) {
- text = mCommands[i].name.c_str();
- } else {
- text = mExeCtx.mCurrentOptions[i].c_str();
- }
-
- SearchResult result{
- .itemIndex = (int)i,
- };
- if (FuzzyMatch::Search(mSearchText.c_str(), text, result.score, result.matches, std::size(result.matches), result.matchCount)) {
- mSearchResults.push_back(result);
- }
- }
-
- std::sort(
- mSearchResults.begin(),
- mSearchResults.end(),
- [](const SearchResult& a, const SearchResult& b) -> bool {
- // We want the biggest element first
- return a.score > b.score;
- });
- }
-
- ImGui::BeginChild("SearchResults", ImVec2(width, 300), false, ImGuiWindowFlags_AlwaysAutoResize);
- auto window = ImGui::GetCurrentWindow();
-
- auto& io = ImGui::GetIO();
- auto dlSharedData = ImGui::GetDrawListSharedData();
-
- auto textColor = ImGui::GetColorU32(ImGuiCol_Text);
- auto itemHoveredColor = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
- auto itemActiveColor = ImGui::GetColorU32(ImGuiCol_HeaderActive);
- auto itemSelectedColor = ImGui::GetColorU32(ImGuiCol_Header);
-
- int itemCount = GetItemCount();
- if (mItems.size() < itemCount) {
- mItems.resize(itemCount);
- }
-
- // Flag used to delay item selection until after the loop ends
- bool selectFocusedItem = false;
- for (size_t i = 0; i < itemCount; ++i) {
- auto id = window->GetID(static_cast<int>(i));
-
- ImVec2 size{
- ImGui::GetContentRegionAvail().x,
- dlSharedData->Font->FontSize,
- };
- ImRect rect{
- window->DC.CursorPos,
- window->DC.CursorPos + ImGui::CalcItemSize(size, 0.0f, 0.0f),
- };
-
- bool& hovered = mItems[i].hovered;
- bool& held = mItems[i].held;
- if (held && hovered) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemActiveColor);
- } else if (hovered) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemHoveredColor);
- } else if (mFocusedItemId == i) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemSelectedColor);
- }
-
- auto item = GetItem(i);
- if (item.indexType == SearchResultIndex) {
- // Iterating search results: draw text with highlights at matched chars
-
- auto& searchResult = mSearchResults[i];
- auto textPos = window->DC.CursorPos;
- int rangeBegin;
- int rangeEnd;
- int lastRangeEnd = 0;
-
- auto DrawCurrentRange = [&]() -> void {
- if (rangeBegin != lastRangeEnd) {
- // Draw normal text between last highlighted range end and current highlighted range start
- auto begin = item.text + lastRangeEnd;
- auto end = item.text + rangeBegin;
- window->DrawList->AddText(textPos, textColor, begin, end);
-
- auto segmentSize = dlSharedData->Font->CalcTextSizeA(dlSharedData->Font->FontSize, std::numeric_limits<float>::max(), 0.0f, begin, end);
- textPos.x += segmentSize.x;
- }
-
- auto begin = item.text + rangeBegin;
- auto end = item.text + rangeEnd;
- window->DrawList->AddText(AppConfig::fontBold, AppConfig::fontBold->FontSize, textPos, textColor, begin, end);
-
- auto segmentSize = AppConfig::fontBold->CalcTextSizeA(AppConfig::fontBold->FontSize, std::numeric_limits<float>::max(), 0.0f, begin, end);
- textPos.x += segmentSize.x;
- };
-
- assert(searchResult.matchCount >= 1);
- rangeBegin = searchResult.matches[0];
- rangeEnd = rangeBegin;
-
- int lastCharIdx = -1;
- for (int j = 0; j < searchResult.matchCount; ++j) {
- int charIdx = searchResult.matches[j];
-
- if (charIdx == lastCharIdx + 1) {
- // These 2 indices are equal, extend our current range by 1
- ++rangeEnd;
- } else {
- DrawCurrentRange();
- lastRangeEnd = rangeEnd;
- rangeBegin = charIdx;
- rangeEnd = charIdx + 1;
- }
-
- lastCharIdx = charIdx;
- }
-
- // Draw the remaining range (if any)
- if (rangeBegin != rangeEnd) {
- DrawCurrentRange();
- }
-
- // Draw the text after the last range (if any)
- window->DrawList->AddText(textPos, textColor, item.text + rangeEnd); // Draw until \0
- } else {
- // Iterating everything else: draw text as-is, there is no highlights
-
- window->DrawList->AddText(window->DC.CursorPos, textColor, item.text);
- }
-
- ImGui::ItemSize(rect);
- if (!ImGui::ItemAdd(rect, id)) {
- continue;
- }
- if (ImGui::ButtonBehavior(rect, id, &hovered, &held)) {
- mFocusedItemId = i;
- selectFocusedItem = true;
- }
- }
-
- if (ImGui::IsKeyPressed(GLFW_KEY_UP)) {
- mFocusedItemId = std::max(mFocusedItemId - 1, 0);
- } else if (ImGui::IsKeyPressed(GLFW_KEY_DOWN)) {
- mFocusedItemId = std::min(mFocusedItemId + 1, itemCount - 1);
- }
- if (ImGui::IsKeyPressed(GLFW_KEY_ENTER) || selectFocusedItem) {
- SelectFocusedItem();
- }
-
- ImGui::EndChild();
-
- ImGui::End();
-}
-
-size_t EditorCommandPalette::GetItemCount() const {
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- if (mSearchText.empty()) {
- return mCommands.size();
- } else {
- return mSearchResults.size();
- }
- } else {
- if (mSearchText.empty()) {
- return mExeCtx.mCurrentOptions.size();
- } else {
- return mSearchResults.size();
- }
- }
-}
-
-EditorCommandPalette::ItemInfo EditorCommandPalette::GetItem(size_t idx) const {
- ItemInfo option;
-
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- if (mSearchText.empty()) {
- option.text = mCommands[idx].name.c_str();
- option.command = &mCommands[idx];
- option.itemId = idx;
- option.indexType = DirectIndex;
- } else {
- auto id = mSearchResults[idx].itemIndex;
- option.text = mCommands[id].name.c_str();
- option.command = &mCommands[id];
- option.itemId = id;
- option.indexType = SearchResultIndex;
- }
- option.itemType = CommandItem;
- } else {
- assert(mExeCtx.GetCurrentCommand() != nullptr);
- if (mSearchText.empty()) {
- option.text = mExeCtx.mCurrentOptions[idx].c_str();
- option.command = mExeCtx.GetCurrentCommand();
- option.itemId = idx;
- option.indexType = DirectIndex;
- } else {
- auto id = mSearchResults[idx].itemIndex;
- option.text = mExeCtx.mCurrentOptions[id].c_str();
- option.command = mExeCtx.GetCurrentCommand();
- option.itemId = id;
- option.indexType = SearchResultIndex;
- }
- option.itemType = CommandOptionItem;
- }
-
- return option;
-}
-
-void EditorCommandPalette::SelectFocusedItem() {
- if (mFocusedItemId < 0 || mFocusedItemId >= GetItemCount()) {
- return;
- }
-
- auto selectedItem = GetItem(mFocusedItemId);
- auto& command = *selectedItem.command;
-
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- assert(!mExeCtx.IsInitiated());
-
- mExeCtx.Initiate(*selectedItem.command);
- if (command.callback) {
- command.callback(mExeCtx);
-
- mFocusSearchBox = true;
- // Don't invalidate search results if no further actions have been requested (returning to global list of commands)
- if (mExeCtx.IsInitiated()) {
- InvalidateSearchResults();
- }
- } else {
- mExeCtx.Finish();
- }
- } else {
- assert(mExeCtx.IsInitiated());
- assert(command.subsequentCallback);
- command.subsequentCallback(mExeCtx, selectedItem.itemId);
-
- mFocusSearchBox = true;
- InvalidateSearchResults();
- }
-
- // This action terminated execution, close command palette window
- if (!mExeCtx.IsInitiated()) {
- if (command.terminate) {
- command.terminate();
- }
- mShouldCloseNextFrame = true;
- }
-}
-
-void EditorCommandPalette::InvalidateSearchResults() {
- mSearchText.clear();
- mSearchResults.clear();
- mFocusedItemId = 0;
-}