ui/ux: Give up on custom ImGui file browsers and just use the system one
This commit is contained in:
parent
7f97416e6e
commit
0af8b8155f
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "external/nativefiledialog"]
|
||||
path = external/nativefiledialog
|
||||
url = https://github.com/btzy/nativefiledialog-extended
|
1
external/ImGui/CMakeLists.txt
vendored
1
external/ImGui/CMakeLists.txt
vendored
@ -19,7 +19,6 @@ add_library(imgui
|
||||
source/imgui_impl_opengl3.cpp
|
||||
source/imgui_tables.cpp
|
||||
source/imgui_widgets.cpp
|
||||
source/ImGuiFileBrowser.cpp
|
||||
source/TextEditor.cpp
|
||||
source/imgui_imhex_extensions.cpp
|
||||
source/imnodes.cpp
|
||||
|
1160
external/ImGui/include/Dirent/dirent.h
vendored
1160
external/ImGui/include/Dirent/dirent.h
vendored
File diff suppressed because it is too large
Load Diff
123
external/ImGui/include/ImGuiFileBrowser.h
vendored
123
external/ImGui/include/ImGuiFileBrowser.h
vendored
@ -1,123 +0,0 @@
|
||||
#ifndef IMGUIFILEBROWSER_H
|
||||
#define IMGUIFILEBROWSER_H
|
||||
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace imgui_addons
|
||||
{
|
||||
class ImGuiFileBrowser
|
||||
{
|
||||
public:
|
||||
ImGuiFileBrowser();
|
||||
~ImGuiFileBrowser();
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
SELECT, //Select Directory Mode
|
||||
OPEN, //Open File mode
|
||||
SAVE //Save File mode.
|
||||
};
|
||||
|
||||
/* Use this to show an open file dialog. The function takes label for the window,
|
||||
* the size, a DialogMode enum value defining in which mode the dialog should operate and optionally the extensions that are valid for opening.
|
||||
* Note that the select directory mode doesn't need any extensions.
|
||||
*/
|
||||
bool showFileDialog(const std::string& label, const DialogMode mode, const ImVec2& sz_xy = ImVec2(0,0), const std::string& valid_types = "*.*");
|
||||
|
||||
/* Store the opened/saved file name or dir name (incase of selectDirectoryDialog) and the absolute path to the selection
|
||||
* Should only be accessed when above functions return true else may contain garbage.
|
||||
*/
|
||||
std::string selected_fn;
|
||||
std::string selected_path;
|
||||
std::string ext; // Store the saved file extension
|
||||
|
||||
|
||||
private:
|
||||
struct Info
|
||||
{
|
||||
Info(std::string name, bool is_hidden) : name(name), is_hidden(is_hidden)
|
||||
{
|
||||
}
|
||||
std::string name;
|
||||
bool is_hidden;
|
||||
};
|
||||
|
||||
//Enum used as bit flags.
|
||||
enum FilterMode
|
||||
{
|
||||
FilterMode_Files = 0x01,
|
||||
FilterMode_Dirs = 0x02
|
||||
};
|
||||
|
||||
//Helper Functions
|
||||
static std::string wStringToString(const wchar_t* wchar_arr);
|
||||
static bool alphaSortComparator(const Info& a, const Info& b);
|
||||
ImVec2 getButtonSize(std::string button_text);
|
||||
|
||||
/* Helper Functions that render secondary modals
|
||||
* and help in validating file extensions and for filtering, parsing top navigation bar.
|
||||
*/
|
||||
void setValidExtTypes(const std::string& valid_types_string);
|
||||
bool validateFile();
|
||||
void showErrorModal();
|
||||
void showInvalidFileModal();
|
||||
bool showReplaceFileModal();
|
||||
void showHelpMarker(std::string desc);
|
||||
void parsePathTabs(std::string str);
|
||||
void filterFiles(int filter_mode);
|
||||
|
||||
/* Core Functions that render the 4 different regions making up
|
||||
* a simple file dialog
|
||||
*/
|
||||
bool renderNavAndSearchBarRegion();
|
||||
bool renderFileListRegion();
|
||||
bool renderInputTextAndExtRegion();
|
||||
bool renderButtonsAndCheckboxRegion();
|
||||
bool renderInputComboBox();
|
||||
void renderExtBox();
|
||||
|
||||
/* Core Functions that handle navigation and
|
||||
* reading directories/files
|
||||
*/
|
||||
bool readDIR(std::string path);
|
||||
bool onNavigationButtonClick(int idx);
|
||||
bool onDirClick(int idx);
|
||||
|
||||
// Functions that reset state and/or clear file list when reading new directory
|
||||
void clearFileList();
|
||||
void closeDialog();
|
||||
|
||||
#if defined (WIN32) || defined (_WIN32) || defined (__WIN32)
|
||||
bool loadWindowsDrives(); // Helper Function for Windows to load Drive Letters.
|
||||
#endif
|
||||
|
||||
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||
void initCurrentPath(); // Helper function for UNIX based system to load Absolute path using realpath
|
||||
#endif
|
||||
|
||||
ImVec2 min_size, max_size, input_combobox_pos, input_combobox_sz;
|
||||
DialogMode dialog_mode;
|
||||
int filter_mode, col_items_limit, selected_idx, selected_ext_idx;
|
||||
float col_width, ext_box_width;
|
||||
bool show_hidden, show_inputbar_combobox, is_dir, is_appearing, filter_dirty, validate_file, path_input_enabled;
|
||||
char input_fn[256];
|
||||
char temp_dir_input[256];
|
||||
|
||||
std::vector<std::string> valid_exts;
|
||||
std::vector<std::string> current_dirlist;
|
||||
std::vector<Info> subdirs;
|
||||
std::vector<Info> subfiles;
|
||||
std::string current_path, error_msg, error_title, invfile_modal_id, repfile_modal_id;
|
||||
|
||||
ImGuiTextFilter filter;
|
||||
std::string valid_types;
|
||||
std::vector<const Info*> filtered_dirs; // Note: We don't need to call delete. It's just for storing filtered items from subdirs and subfiles so we don't use PassFilter every frame.
|
||||
std::vector<const Info*> filtered_files;
|
||||
std::vector< std::reference_wrapper<std::string> > inputcb_filter_files;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // IMGUIFILEBROWSER_H
|
1214
external/ImGui/source/ImGuiFileBrowser.cpp
vendored
1214
external/ImGui/source/ImGuiFileBrowser.cpp
vendored
File diff suppressed because it is too large
Load Diff
1
external/nativefiledialog
vendored
Submodule
1
external/nativefiledialog
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit fbd8480bd63b8b9d808e3206acccd1cb113bac8b
|
@ -5,7 +5,6 @@
|
||||
#include "helpers/encoding_file.hpp"
|
||||
|
||||
#include <imgui_memory_editor.h>
|
||||
#include <ImGuiFileBrowser.h>
|
||||
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <ImGuiFileBrowser.h>
|
||||
#include <TextEditor.h>
|
||||
|
||||
namespace hex {
|
||||
|
@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../external/ImGui ${CMAKE_CURRENT_BINARY_DIR}/external/ImGui)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../external/nlohmann_json ${CMAKE_CURRENT_BINARY_DIR}/external/nlohmann_json)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../external/nativefiledialog ${CMAKE_CURRENT_BINARY_DIR}/external/nativefiledialog)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
@ -42,7 +43,7 @@ target_include_directories(libimhex PUBLIC include ${MBEDTLS_INCLUDE_DIR})
|
||||
target_link_directories(libimhex PUBLIC ${MBEDTLS_LIBRARY_DIR})
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(libimhex PUBLIC imgui nlohmann_json libmbedcrypto.a)
|
||||
target_link_libraries(libimhex PUBLIC imgui nlohmann_json libmbedcrypto.a nfd)
|
||||
else ()
|
||||
target_link_libraries(libimhex PUBLIC imgui nlohmann_json mbedcrypto)
|
||||
target_link_libraries(libimhex PUBLIC imgui nlohmann_json mbedcrypto nfd)
|
||||
endif ()
|
@ -13,7 +13,6 @@
|
||||
#include <hex/views/view.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <ImGuiFileBrowser.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@ -71,12 +70,6 @@ namespace hex {
|
||||
static std::vector<ContentRegistry::Interface::DrawCallback> welcomeScreenEntries;
|
||||
static std::vector<ContentRegistry::Interface::DrawCallback> footerItems;
|
||||
|
||||
static imgui_addons::ImGuiFileBrowser fileBrowser;
|
||||
static imgui_addons::ImGuiFileBrowser::DialogMode fileBrowserDialogMode;
|
||||
static std::string fileBrowserTitle;
|
||||
static std::string fileBrowserValidExtensions;
|
||||
static std::function<void(std::string)> fileBrowserCallback;
|
||||
|
||||
static std::vector<ContentRegistry::DataProcessorNode::Entry> dataProcessorNodes;
|
||||
static u32 dataProcessorNodeIdCounter;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <ImGuiFileBrowser.h>
|
||||
#include <nfd.hpp>
|
||||
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
@ -27,7 +27,13 @@ namespace hex {
|
||||
virtual bool isAvailable();
|
||||
virtual bool shouldProcess() { return this->isAvailable() && this->getWindowOpenState(); }
|
||||
|
||||
static void openFileBrowser(std::string title, imgui_addons::ImGuiFileBrowser::DialogMode mode, std::string validExtensions, const std::function<void(std::string)> &callback);
|
||||
enum class DialogMode {
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
static void openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback);
|
||||
static void doLater(std::function<void()> &&function);
|
||||
static std::vector<std::function<void()>>& getDeferedCalls();
|
||||
|
||||
|
@ -25,12 +25,6 @@ namespace hex {
|
||||
std::vector<ContentRegistry::Interface::DrawCallback> SharedData::welcomeScreenEntries;
|
||||
std::vector<ContentRegistry::Interface::DrawCallback> SharedData::footerItems;
|
||||
|
||||
imgui_addons::ImGuiFileBrowser SharedData::fileBrowser;
|
||||
imgui_addons::ImGuiFileBrowser::DialogMode SharedData::fileBrowserDialogMode;
|
||||
std::string SharedData::fileBrowserTitle;
|
||||
std::string SharedData::fileBrowserValidExtensions;
|
||||
std::function<void(std::string)> SharedData::fileBrowserCallback;
|
||||
|
||||
std::vector<ContentRegistry::DataProcessorNode::Entry> SharedData::dataProcessorNodes;
|
||||
u32 SharedData::dataProcessorNodeIdCounter = 1;
|
||||
|
||||
|
@ -28,15 +28,30 @@ namespace hex {
|
||||
return EventManager::post(eventType, userData);
|
||||
}
|
||||
|
||||
void View::openFileBrowser(std::string title, imgui_addons::ImGuiFileBrowser::DialogMode mode, std::string validExtensions, const std::function<void(std::string)> &callback) {
|
||||
SharedData::fileBrowserTitle = title;
|
||||
SharedData::fileBrowserDialogMode = mode;
|
||||
SharedData::fileBrowserValidExtensions = std::move(validExtensions);
|
||||
SharedData::fileBrowserCallback = callback;
|
||||
void View::openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback) {
|
||||
NFD::Init();
|
||||
|
||||
View::doLater([title]{
|
||||
ImGui::OpenPopup(title.c_str());
|
||||
});
|
||||
nfdchar_t *outPath;
|
||||
nfdresult_t result;
|
||||
switch (mode) {
|
||||
case DialogMode::Open:
|
||||
result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Save:
|
||||
result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Folder:
|
||||
result = NFD::PickFolder(outPath, nullptr);
|
||||
break;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
callback(outPath);
|
||||
NFD::FreePath(outPath);
|
||||
}
|
||||
|
||||
NFD::Quit();
|
||||
}
|
||||
|
||||
void View::drawCommonInterfaces() {
|
||||
@ -49,11 +64,6 @@ namespace hex {
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (SharedData::fileBrowser.showFileDialog(SharedData::fileBrowserTitle, SharedData::fileBrowserDialogMode, ImVec2(0, 0), SharedData::fileBrowserValidExtensions)) {
|
||||
SharedData::fileBrowserCallback(SharedData::fileBrowser.selected_path);
|
||||
SharedData::fileBrowserTitle = "";
|
||||
}
|
||||
}
|
||||
|
||||
void View::showErrorPopup(std::string_view errorMessage) {
|
||||
|
@ -174,12 +174,12 @@ namespace hex {
|
||||
|
||||
View::subscribeEvent(Events::OpenWindow, [this](auto name) {
|
||||
if (std::any_cast<const char*>(name) == std::string("Open File")) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->openFile(path);
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
} else if (std::any_cast<const char*>(name) == std::string("Open Project")) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_project"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ".hexproj", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
ProjectFile::load(path);
|
||||
View::postEvent(Events::ProjectFileLoad);
|
||||
this->getWindowOpenState() = true;
|
||||
@ -246,7 +246,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
static void saveAs() {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_as"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, "*.*", [](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_as"_lang, View::DialogMode::Save, { }, [](auto path) {
|
||||
FILE *file = fopen(path.c_str(), "wb");
|
||||
|
||||
if (file != nullptr) {
|
||||
@ -293,14 +293,14 @@ namespace hex {
|
||||
ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.view.hexeditor.script.script"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.script.title"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ".py", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.script.title"_lang, DialogMode::Open, { { "Python Script", "py" } }, [this](auto path) {
|
||||
this->m_loaderScriptScriptPath = path;
|
||||
});
|
||||
}
|
||||
ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.view.hexeditor.script.file"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.file.title"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.file.title"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->m_loaderScriptFilePath = path;
|
||||
});
|
||||
}
|
||||
@ -353,7 +353,7 @@ namespace hex {
|
||||
if (ImGui::BeginMenu("hex.menu.file"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.open_file"_lang, "CTRL + O")) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->openFile(path);
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
@ -370,7 +370,7 @@ namespace hex {
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.open_project"_lang, "")) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.open_project"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ".hexproj", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
ProjectFile::load(path);
|
||||
View::postEvent(Events::ProjectFileLoad);
|
||||
this->getWindowOpenState() = true;
|
||||
@ -381,7 +381,7 @@ namespace hex {
|
||||
View::postEvent(Events::ProjectFileStore);
|
||||
|
||||
if (ProjectFile::getProjectFilePath() == "") {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_project"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, ".hexproj", [](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_project"_lang, DialogMode::Save, { { "Project File", "hexproj" } }, [](auto path) {
|
||||
ProjectFile::store(path);
|
||||
});
|
||||
}
|
||||
@ -390,7 +390,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.load_encoding_file"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.load_enconding_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.load_enconding_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path);
|
||||
});
|
||||
}
|
||||
@ -400,7 +400,7 @@ namespace hex {
|
||||
if (ImGui::BeginMenu("hex.view.hexeditor.menu.file.import"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.base64"_lang)) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.import.base64"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.import.base64"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
std::vector<u8> base64;
|
||||
this->loadFromFile(path, base64);
|
||||
|
||||
@ -420,7 +420,7 @@ namespace hex {
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.ips"_lang)) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
auto patchData = hex::readFile(path);
|
||||
auto patch = hex::loadIPSPatch(patchData);
|
||||
|
||||
@ -434,7 +434,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.ips32"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
auto patchData = hex::readFile(path);
|
||||
auto patch = hex::loadIPS32Patch(patchData);
|
||||
|
||||
@ -464,7 +464,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
this->m_dataToSave = generateIPSPatch(patches);
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
this->saveToFile(path, this->m_dataToSave);
|
||||
});
|
||||
}
|
||||
@ -477,7 +477,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
this->m_dataToSave = generateIPS32Patch(patches);
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, "*.*", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
this->saveToFile(path, this->m_dataToSave);
|
||||
});
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ namespace hex {
|
||||
void ViewPattern::drawMenu() {
|
||||
if (ImGui::BeginMenu("hex.menu.file"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.pattern.menu.file.load_pattern"_lang)) {
|
||||
View::openFileBrowser("hex.view.pattern.open_pattern"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ".hexpat", [this](auto path) {
|
||||
View::openFileBrowser("hex.view.pattern.open_pattern"_lang, DialogMode::Open, { { "Pattern File", "hexpat" } }, [this](auto path) {
|
||||
this->loadPatternFile(path);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user