1
0
mirror of synced 2024-11-24 15:50:16 +01:00

feat: Allow toolbar icons to be modified (#1509)

This commit is contained in:
Nik 2024-01-21 18:39:32 +01:00 committed by GitHub
parent a13b5bf8c0
commit d005b5d2d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 364 additions and 98 deletions

View File

@ -103,8 +103,8 @@ if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
target_link_libraries(libimhex PUBLIC ${FOUNDATION}) target_link_libraries(libimhex PUBLIC ${FOUNDATION})
endif () endif ()
target_link_libraries(libimhex PRIVATE microtar libpl plcli libpl-gen libwolv ${NFD_LIBRARIES} magic dl ${NLOHMANN_JSON_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} ${JTHREAD_LIBRARIES}) target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${NLOHMANN_JSON_LIBRARIES} ${MBEDTLS_LIBRARIES} ${JTHREAD_LIBRARIES})
target_link_libraries(libimhex PUBLIC ${IMGUI_LIBRARIES}) target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
endif() endif()
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES}) target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES})

View File

@ -18,6 +18,7 @@
using ImGuiDataType = int; using ImGuiDataType = int;
using ImGuiInputTextFlags = int; using ImGuiInputTextFlags = int;
struct ImColor; struct ImColor;
enum ImGuiCustomCol : int;
namespace hex { namespace hex {
@ -656,6 +657,13 @@ namespace hex {
/* Interface Registry. Allows adding new items to various interfaces */ /* Interface Registry. Allows adding new items to various interfaces */
namespace Interface { namespace Interface {
struct Icon {
Icon(const char *glyph, ImGuiCustomCol color = ImGuiCustomCol(0x00)) : glyph(glyph), color(color) {}
std::string glyph;
ImGuiCustomCol color;
};
namespace impl { namespace impl {
using DrawCallback = std::function<void()>; using DrawCallback = std::function<void()>;
@ -670,12 +678,13 @@ namespace hex {
struct MenuItem { struct MenuItem {
std::vector<UnlocalizedString> unlocalizedNames; std::vector<UnlocalizedString> unlocalizedNames;
const char *icon; Icon icon;
std::unique_ptr<Shortcut> shortcut; std::unique_ptr<Shortcut> shortcut;
View *view; View *view;
MenuCallback callback; MenuCallback callback;
EnabledCallback enabledCallback; EnabledCallback enabledCallback;
SelectedCallback selectedCallback; SelectedCallback selectedCallback;
i32 toolbarIndex;
}; };
struct SidebarItem { struct SidebarItem {
@ -723,7 +732,7 @@ namespace hex {
*/ */
void addMenuItem( void addMenuItem(
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
const char *icon, const Icon &icon,
u32 priority, u32 priority,
const Shortcut &shortcut, const Shortcut &shortcut,
const impl::MenuCallback &function, const impl::MenuCallback &function,
@ -743,7 +752,7 @@ namespace hex {
*/ */
void addMenuItem( void addMenuItem(
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
const char *icon, const Icon &icon,
u32 priority, u32 priority,
const Shortcut &shortcut, const Shortcut &shortcut,
const impl::MenuCallback &function, const impl::MenuCallback &function,
@ -829,6 +838,13 @@ namespace hex {
*/ */
void addToolbarItem(const impl::DrawCallback &function); void addToolbarItem(const impl::DrawCallback &function);
/**
* @brief Adds a menu item to the toolbar
* @param unlocalizedName Unlocalized name of the menu item
* @param color Color of the toolbar icon
*/
void addMenuItemToToolbar(const UnlocalizedString &unlocalizedName, ImGuiCustomCol color);
/** /**
* @brief Adds a new sidebar item * @brief Adds a new sidebar item
* @param icon The icon to use for the item * @param icon The icon to use for the item

View File

@ -3,6 +3,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector>
#include <fmt/format.h> #include <fmt/format.h>

View File

@ -14,7 +14,7 @@
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
enum ImGuiCustomCol { enum ImGuiCustomCol : int {
ImGuiCustomCol_DescButton, ImGuiCustomCol_DescButton,
ImGuiCustomCol_DescButtonHovered, ImGuiCustomCol_DescButtonHovered,
ImGuiCustomCol_DescButtonActive, ImGuiCustomCol_DescButtonActive,

View File

@ -148,8 +148,6 @@ namespace hex {
const auto entry = insertOrGetEntry(subCategory->entries, unlocalizedName); const auto entry = insertOrGetEntry(subCategory->entries, unlocalizedName);
entry->widget = std::move(widget); entry->widget = std::move(widget);
entry->widget->load(getSetting(unlocalizedCategory, unlocalizedName, entry->widget->store()));
entry->widget->onChanged();
return entry->widget.get(); return entry->widget.get();
} }
@ -723,22 +721,26 @@ namespace hex {
} }
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) {
addMenuItem(unlocalizedMainMenuNames, nullptr, priority, shortcut, function, enabledCallback, selectedCallback, view); addMenuItem(unlocalizedMainMenuNames, "", priority, shortcut, function, enabledCallback, selectedCallback, view);
} }
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const char *icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const Icon &icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {
addMenuItem(unlocalizedMainMenuNames, icon, priority, shortcut, function, enabledCallback, []{ return false; }, view); addMenuItem(unlocalizedMainMenuNames, icon, priority, shortcut, function, enabledCallback, []{ return false; }, view);
} }
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) { void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, View *view) {
addMenuItem(unlocalizedMainMenuNames, nullptr, priority, shortcut, function, enabledCallback, []{ return false; }, view); addMenuItem(unlocalizedMainMenuNames, "", priority, shortcut, function, enabledCallback, []{ return false; }, view);
} }
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const char *icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) { void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, const Icon &icon, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback, const impl::SelectedCallback &selectedCallback, View *view) {
log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority); log::debug("Added new menu item to menu {} with priority {}", unlocalizedMainMenuNames[0].get(), priority);
Icon coloredIcon = icon;
if (coloredIcon.color == 0x00)
coloredIcon.color = ImGuiCustomCol_ToolbarGray;
impl::getMenuItems().insert({ impl::getMenuItems().insert({
priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique<Shortcut>(shortcut), view, function, enabledCallback, selectedCallback } priority, impl::MenuItem { unlocalizedMainMenuNames, coloredIcon, std::make_unique<Shortcut>(shortcut), view, function, enabledCallback, selectedCallback, -1 }
}); });
if (shortcut != Shortcut::None) { if (shortcut != Shortcut::None) {
@ -750,7 +752,7 @@ namespace hex {
} }
void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) { void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
addMenuItemSubMenu(std::move(unlocalizedMainMenuNames), nullptr, priority, function, enabledCallback); addMenuItemSubMenu(std::move(unlocalizedMainMenuNames), "", priority, function, enabledCallback);
} }
void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) { void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, const char *icon, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback) {
@ -758,14 +760,14 @@ namespace hex {
unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue); unlocalizedMainMenuNames.emplace_back(impl::SubMenuValue);
impl::getMenuItems().insert({ impl::getMenuItems().insert({
priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique<Shortcut>(), nullptr, function, enabledCallback, []{ return false; } } priority, impl::MenuItem { unlocalizedMainMenuNames, icon, std::make_unique<Shortcut>(), nullptr, function, enabledCallback, []{ return false; }, -1 }
}); });
} }
void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority) { void addMenuItemSeparator(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority) {
unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue); unlocalizedMainMenuNames.emplace_back(impl::SeparatorValue);
impl::getMenuItems().insert({ impl::getMenuItems().insert({
priority, impl::MenuItem { unlocalizedMainMenuNames, "", std::make_unique<Shortcut>(), nullptr, []{}, []{ return true; }, []{ return false; } } priority, impl::MenuItem { unlocalizedMainMenuNames, "", std::make_unique<Shortcut>(), nullptr, []{}, []{ return true; }, []{ return false; }, -1 }
}); });
} }
@ -781,6 +783,21 @@ namespace hex {
impl::getToolbarItems().push_back(function); impl::getToolbarItems().push_back(function);
} }
void addMenuItemToToolbar(const UnlocalizedString& unlocalizedName, ImGuiCustomCol color) {
auto maxIndex = std::ranges::max_element(impl::getMenuItems(), [](const auto &a, const auto &b) {
return a.second.toolbarIndex < b.second.toolbarIndex;
})->second.toolbarIndex;
for (auto &[priority, menuItem] : impl::getMenuItems()) {
if (menuItem.unlocalizedNames.back() == unlocalizedName) {
menuItem.toolbarIndex = maxIndex + 1;
menuItem.icon.color = color;
break;
}
}
}
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback) { void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback) {
impl::getSidebarItems().push_back({ icon, function, enabledCallback }); impl::getSidebarItems().push_back({ icon, function, enabledCallback });
} }

View File

@ -79,6 +79,7 @@ namespace hex {
Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { } Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { }
Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { } Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
Lang::operator std::string() const { Lang::operator std::string() const {
return get(); return get();
} }

View File

@ -54,7 +54,7 @@ set_target_properties(main PROPERTIES
target_compile_definitions(main PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}") target_compile_definitions(main PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
target_link_libraries(main PRIVATE libromfs-imhex libimhex libwolv libpl plcli libpl-gen ${FMT_LIBRARIES} LLVMDemangle ${LIBBACKTRACE_LIBRARIES}) target_link_libraries(main PRIVATE libromfs-imhex libimhex libwolv ${LIBBACKTRACE_LIBRARIES} LLVMDemangle)
if (WIN32) if (WIN32)
target_link_libraries(main PRIVATE usp10 wsock32 ws2_32 Dwmapi.lib) target_link_libraries(main PRIVATE usp10 wsock32 ws2_32 Dwmapi.lib)
else () else ()

View File

@ -519,9 +519,9 @@ namespace hex {
} }
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallack] = menuItem; const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallack, toolbarIndex] = menuItem;
createNestedMenu(unlocalizedNames, icon, *shortcut, callback, enabledCallback, selectedCallack); createNestedMenu(unlocalizedNames, icon.glyph.c_str(), *shortcut, callback, enabledCallback, selectedCallack);
} }
}; };
@ -792,10 +792,10 @@ namespace hex {
// Draw main menu popups // Draw main menu popups
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallback] = menuItem; const auto &[unlocalizedNames, icon, shortcut, view, callback, enabledCallback, selectedCallback, toolbarIndex] = menuItem;
if (ImGui::BeginPopup(unlocalizedNames.front().get().c_str())) { if (ImGui::BeginPopup(unlocalizedNames.front().get().c_str())) {
createNestedMenu({ unlocalizedNames.begin() + 1, unlocalizedNames.end() }, icon, *shortcut, callback, enabledCallback, selectedCallback); createNestedMenu({ unlocalizedNames.begin() + 1, unlocalizedNames.end() }, icon.glyph.c_str(), *shortcut, callback, enabledCallback, selectedCallback);
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }

View File

@ -5,7 +5,7 @@ add_executable(updater
) )
target_compile_definitions(updater PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}") target_compile_definitions(updater PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
target_link_libraries(updater PRIVATE libimhex wolv::io ${FMT_LIBRARIES}) target_link_libraries(updater PRIVATE libimhex ${FMT_LIBRARIES})
add_dependencies(main updater) add_dependencies(main updater)
if (APPLE) if (APPLE)

View File

@ -483,6 +483,9 @@
"hex.builtin.setting.proxy.url.tooltip": "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)", "hex.builtin.setting.proxy.url.tooltip": "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)",
"hex.builtin.setting.shortcuts": "Shortcuts", "hex.builtin.setting.shortcuts": "Shortcuts",
"hex.builtin.setting.shortcuts.global": "Global Shortcuts", "hex.builtin.setting.shortcuts.global": "Global Shortcuts",
"hex.builtin.setting.toolbar": "Toolbar",
"hex.builtin.setting.toolbar.description": "Add or remove menu options to the toolbar.",
"hex.builtin.setting.toolbar.icons": "Toolbar Icons",
"hex.builtin.shortcut.next_provider": "Select next provider", "hex.builtin.shortcut.next_provider": "Select next provider",
"hex.builtin.shortcut.prev_provider": "Select previous provider", "hex.builtin.shortcut.prev_provider": "Select previous provider",
"hex.builtin.title_bar_button.debug_build": "Debug build", "hex.builtin.title_bar_button.debug_build": "Debug build",

View File

@ -351,7 +351,7 @@ namespace hex::plugin::builtin {
}, noRunningTasks); }, noRunningTasks);
/* Open File */ /* Open File */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.open_file" }, ICON_VS_OPEN_PREVIEW, 1100, CTRLCMD + Keys::O, [] { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.open_file" }, ICON_VS_FOLDER_OPENED, 1100, CTRLCMD + Keys::O, [] {
RequestOpenWindow::post("Open File"); RequestOpenWindow::post("Open File");
}, noRunningTasks); }, noRunningTasks);

View File

@ -360,6 +360,247 @@ namespace hex::plugin::builtin {
bool m_hasDuplicate = false; bool m_hasDuplicate = false;
}; };
class ToolbarIconsWidget : public ContentRegistry::Settings::Widgets::Widget {
private:
struct MenuItemSorter {
bool operator()(const auto *a, const auto *b) const {
return a->toolbarIndex < b->toolbarIndex;
}
};
public:
bool draw(const std::string &) override {
bool changed = false;
// Top level layout table
if (ImGui::BeginTable("##top_level", 2, ImGuiTableFlags_None, ImGui::GetContentRegionAvail())) {
ImGui::TableSetupColumn("##left", ImGuiTableColumnFlags_WidthStretch, 0.3F);
ImGui::TableSetupColumn("##right", ImGuiTableColumnFlags_WidthStretch, 0.7F);
ImGui::TableNextRow();
ImGui::TableNextColumn();
// Draw list of menu items that can be added to the toolbar
if (ImGui::BeginTable("##all_icons", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 280_scaled))) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
// Loop over all available menu items
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
// Filter out items without icon, separators, submenus and items that are already in the toolbar
if (menuItem.icon.glyph.empty())
continue;
const auto &unlocalizedName = menuItem.unlocalizedNames.back();
if (menuItem.unlocalizedNames.size() > 2)
continue;
if (unlocalizedName.get() == ContentRegistry::Interface::impl::SeparatorValue)
continue;
if (unlocalizedName.get() == ContentRegistry::Interface::impl::SubMenuValue)
continue;
if (menuItem.toolbarIndex != -1)
continue;
ImGui::TableNextRow();
ImGui::TableNextColumn();
// Draw the menu item
ImGui::Selectable(hex::format("{} {}", menuItem.icon.glyph, Lang(unlocalizedName)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns);
// Handle draggin the menu item to the toolbar box
if (ImGui::BeginDragDropSource()) {
auto ptr = &menuItem;
ImGui::SetDragDropPayload("MENU_ITEM_PAYLOAD", &ptr, sizeof(void*));
ImGuiExt::TextFormatted("{} {}", menuItem.icon.glyph, Lang(unlocalizedName));
ImGui::EndDragDropSource();
}
}
ImGui::EndTable();
}
// Handle dropping menu items from the toolbar box
if (ImGui::BeginDragDropTarget()) {
if (auto payload = ImGui::AcceptDragDropPayload("TOOLBAR_ITEM_PAYLOAD"); payload != nullptr) {
auto &menuItem = *static_cast<ContentRegistry::Interface::impl::MenuItem **>(payload->Data);
menuItem->toolbarIndex = -1;
changed = true;
}
ImGui::EndDragDropTarget();
}
ImGui::TableNextColumn();
// Draw toolbar icon box
ImGuiExt::BeginSubWindow("hex.builtin.setting.toolbar.icons"_lang, ImGui::GetContentRegionAvail());
{
if (ImGui::BeginTable("##icons", 6, ImGuiTableFlags_SizingStretchSame, ImGui::GetContentRegionAvail())) {
ImGui::TableNextRow();
// Find all menu items that are in the toolbar and sort them by their toolbar index
std::set<ContentRegistry::Interface::impl::MenuItem*, MenuItemSorter> toolbarItems;
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
if (menuItem.toolbarIndex == -1)
continue;
toolbarItems.emplace(&menuItem);
}
// Loop over all toolbar items
for (auto &menuItem : toolbarItems) {
// Filter out items without icon, separators, submenus and items that are not in the toolbar
if (menuItem->icon.glyph.empty())
continue;
const auto &unlocalizedName = menuItem->unlocalizedNames.back();
if (menuItem->unlocalizedNames.size() > 2)
continue;
if (unlocalizedName.get() == ContentRegistry::Interface::impl::SubMenuValue)
continue;
if (menuItem->toolbarIndex == -1)
continue;
ImGui::TableNextColumn();
// Draw the toolbar item
ImGui::InvisibleButton(unlocalizedName.get().c_str(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x));
// Handle dragging the toolbar item around
if (ImGui::BeginDragDropSource()) {
ImGui::SetDragDropPayload("TOOLBAR_ITEM_PAYLOAD", &menuItem, sizeof(void*));
ImGuiExt::TextFormatted("{} {}", menuItem->icon.glyph, Lang(unlocalizedName));
ImGui::EndDragDropSource();
}
// Handle dropping toolbar items onto each other to reorder them
if (ImGui::BeginDragDropTarget()) {
if (auto payload = ImGui::AcceptDragDropPayload("TOOLBAR_ITEM_PAYLOAD"); payload != nullptr) {
auto &otherMenuItem = *static_cast<ContentRegistry::Interface::impl::MenuItem **>(payload->Data);
std::swap(menuItem->toolbarIndex, otherMenuItem->toolbarIndex);
changed = true;
}
ImGui::EndDragDropTarget();
}
// Handle right clicking toolbar items to open the color selection popup
if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
ImGui::OpenPopup(unlocalizedName.get().c_str());
// Draw the color selection popup
if (ImGui::BeginPopup(unlocalizedName.get().c_str())) {
constexpr static std::array Colors = {
ImGuiCustomCol_ToolbarGray,
ImGuiCustomCol_ToolbarRed,
ImGuiCustomCol_ToolbarYellow,
ImGuiCustomCol_ToolbarGreen,
ImGuiCustomCol_ToolbarBlue,
ImGuiCustomCol_ToolbarPurple,
ImGuiCustomCol_ToolbarBrown
};
// Draw all the color buttons
for (auto color : Colors) {
ImGui::PushID(&color);
if (ImGui::ColorButton(hex::format("##color{}", u32(color)).c_str(), ImGuiExt::GetCustomColorVec4(color), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker, ImVec2(20, 20))) {
menuItem->icon.color = color;
ImGui::CloseCurrentPopup();
changed = true;
}
ImGui::PopID();
ImGui::SameLine();
}
ImGui::EndPopup();
}
auto min = ImGui::GetItemRectMin();
auto max = ImGui::GetItemRectMax();
auto iconSize = ImGui::CalcTextSize(menuItem->icon.glyph.c_str());
auto text = Lang(unlocalizedName).get();
if (text.ends_with("..."))
text = text.substr(0, text.size() - 3);
auto textSize = ImGui::CalcTextSize(text.c_str());
// Draw icon and text of the toolbar item
auto drawList = ImGui::GetWindowDrawList();
drawList->AddText((min + ((max - min) - iconSize) / 2) - ImVec2(0, 10_scaled), ImGuiExt::GetCustomColorU32(ImGuiCustomCol(menuItem->icon.color)), menuItem->icon.glyph.c_str());
drawList->AddText((min + ((max - min)) / 2) + ImVec2(-textSize.x / 2, 5_scaled), ImGui::GetColorU32(ImGuiCol_Text), text.c_str());
}
ImGui::EndTable();
}
}
ImGuiExt::EndSubWindow();
// Handle dropping menu items onto the toolbar box
if (ImGui::BeginDragDropTarget()) {
if (auto payload = ImGui::AcceptDragDropPayload("MENU_ITEM_PAYLOAD"); payload != nullptr) {
auto &menuItem = *static_cast<ContentRegistry::Interface::impl::MenuItem **>(payload->Data);
menuItem->toolbarIndex = m_currIndex;
m_currIndex += 1;
changed = true;
}
ImGui::EndDragDropTarget();
}
ImGui::EndTable();
}
return changed;
}
nlohmann::json store() override {
std::map<i32, std::pair<std::string, u32>> items;
for (const auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
if (menuItem.toolbarIndex != -1)
items.emplace(menuItem.toolbarIndex, std::make_pair(menuItem.unlocalizedNames.back().get(), menuItem.icon.color));
}
return items;
}
void load(const nlohmann::json &data) override {
if (data.is_null())
return;
auto toolbarItems = data.get<std::map<i32, std::pair<std::string, u32>>>();
if (toolbarItems.empty())
return;
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems())
menuItem.toolbarIndex = -1;
for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
for (const auto &[index, value] : toolbarItems) {
const auto &[name, color] = value;
if (menuItem.unlocalizedNames.back().get() == name) {
menuItem.toolbarIndex = index;
menuItem.icon.color = ImGuiCustomCol(color);
break;
}
}
}
m_currIndex = toolbarItems.size();
}
private:
i32 m_currIndex = 0;
};
} }
void registerSettings() { void registerSettings() {
@ -536,6 +777,11 @@ namespace hex::plugin::builtin {
} }
}); });
/* Toolbar icons */
ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.toolbar", "hex.builtin.setting.toolbar.description");
ContentRegistry::Settings::add<ToolbarIconsWidget>("hex.builtin.setting.toolbar", "", "hex.builtin.setting.toolbar.icons");
} }
static void loadLayoutSettings() { static void loadLayoutSettings() {

View File

@ -233,6 +233,12 @@ namespace hex::plugin::builtin {
} }
} }
struct MenuItemSorter {
bool operator()(const auto *a, const auto *b) const {
return a->toolbarIndex < b->toolbarIndex;
}
};
void addToolbarItems() { void addToolbarItems() {
ShortcutManager::addGlobalShortcut(AllowWhileTyping + ALT + CTRLCMD + Keys::Left, "hex.builtin.shortcut.prev_provider", []{ ShortcutManager::addGlobalShortcut(AllowWhileTyping + ALT + CTRLCMD + Keys::Left, "hex.builtin.shortcut.prev_provider", []{
auto currIndex = ImHexApi::Provider::getCurrentProviderIndex(); auto currIndex = ImHexApi::Provider::getCurrentProviderIndex();
@ -274,87 +280,39 @@ namespace hex::plugin::builtin {
}); });
ContentRegistry::Interface::addToolbarItem([] { ContentRegistry::Interface::addToolbarItem([] {
auto provider = ImHexApi::Provider::get(); std::set<const ContentRegistry::Interface::impl::MenuItem*, MenuItemSorter> menuItems;
bool providerValid = provider != nullptr;
bool tasksRunning = TaskManager::getRunningTaskCount() > 0;
// Undo for (const auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) {
ImGui::BeginDisabled(!providerValid || !provider->canUndo()); if (menuItem.toolbarIndex != -1) {
{ menuItems.insert(&menuItem);
if (ImGuiExt::ToolBarButton(ICON_VS_DISCARD, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
provider->undo();
} }
ImGui::EndDisabled();
// Redo
ImGui::BeginDisabled(!providerValid || !provider->canRedo());
{
if (ImGuiExt::ToolBarButton(ICON_VS_REDO, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
provider->redo();
} }
ImGui::EndDisabled();
for (const auto &menuItem : menuItems) {
if (menuItem->unlocalizedNames.back().get() == ContentRegistry::Interface::impl::SeparatorValue) {
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
continue;
ImGui::BeginDisabled(tasksRunning);
{
// Create new file
if (ImGuiExt::ToolBarButton(ICON_VS_FILE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGray))) {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
hex::ImHexApi::Provider::remove(newProvider);
else
EventProviderOpened::post(newProvider);
} }
// Open file ImGui::BeginDisabled(!menuItem->enabledCallback());
if (ImGuiExt::ToolBarButton(ICON_VS_FOLDER_OPENED, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarBrown))) if (ImGuiExt::ToolBarButton(menuItem->icon.glyph.c_str(), ImGuiExt::GetCustomColorVec4(ImGuiCustomCol(menuItem->icon.color)))) {
RequestOpenWindow::post("Open File"); menuItem->callback();
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
// Save file
ImGui::BeginDisabled(!providerValid || !provider->isWritable() || !provider->isSavable());
{
if (ImGuiExt::ToolBarButton(ICON_VS_SAVE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
provider->save();
} }
ImGui::EndDisabled();
// Save file as
ImGui::BeginDisabled(!providerValid || !provider->isSavable());
{
if (ImGuiExt::ToolBarButton(ICON_VS_SAVE_AS, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue)))
fs::openFileBrowser(fs::DialogMode::Save, {}, [&provider](auto path) {
provider->saveAs(path);
}); });
}
ImGui::EndDisabled();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
// Create bookmark
ImGui::BeginDisabled(!providerValid || !provider->isReadable() || !ImHexApi::HexEditor::isSelectionValid());
{
if (ImGuiExt::ToolBarButton(ICON_VS_BOOKMARK, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
auto region = ImHexApi::HexEditor::getSelection();
if (region.has_value())
ImHexApi::Bookmarks::add(region->getStartAddress(), region->getSize(), {}, {});
}
}
ImGui::EndDisabled();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
// Provider switcher // Provider switcher
ContentRegistry::Interface::addToolbarItem([] {
const auto provider = ImHexApi::Provider::get();
const bool providerValid = provider != nullptr;
const bool tasksRunning = TaskManager::getRunningTaskCount() > 0;
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
ImGui::BeginDisabled(!providerValid || tasksRunning); ImGui::BeginDisabled(!providerValid || tasksRunning);
{ {
auto &providers = ImHexApi::Provider::getProviders(); auto &providers = ImHexApi::Provider::getProviders();
@ -446,6 +404,14 @@ namespace hex::plugin::builtin {
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
}); });
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.menu.edit.undo", ImGuiCustomCol_ToolbarBlue);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.menu.edit.redo", ImGuiCustomCol_ToolbarBlue);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.menu.file.create_file", ImGuiCustomCol_ToolbarGray);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.menu.file.open_file", ImGuiCustomCol_ToolbarBrown);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.file.save", ImGuiCustomCol_ToolbarBlue);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.view.hex_editor.menu.file.save_as", ImGuiCustomCol_ToolbarBlue);
ContentRegistry::Interface::addMenuItemToToolbar("hex.builtin.menu.edit.bookmark.create", ImGuiCustomCol_ToolbarGreen);
} }
void handleBorderlessWindowMode() { void handleBorderlessWindowMode() {

View File

@ -24,10 +24,26 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.settings.name" }, ICON_VS_SETTINGS_GEAR, 4000, Shortcut::None, [&, this] { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.settings.name" }, ICON_VS_SETTINGS_GEAR, 4000, Shortcut::None, [&, this] {
this->getWindowOpenState() = true; this->getWindowOpenState() = true;
}); });
EventImHexStartupFinished::subscribe(this, [] {
for (const auto &[unlocalizedCategory, unlocalizedDescription, subCategories] : ContentRegistry::Settings::impl::getSettings()) {
for (const auto &[unlocalizedSubCategory, entries] : subCategories) {
for (const auto &[unlocalizedName, widget] : entries) {
try {
widget->load(ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, widget->store()));
widget->onChanged();
} catch (const std::exception &e) {
log::error("Failed to load setting [{} / {}]: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what());
}
}
}
}
});
} }
ViewSettings::~ViewSettings() { ViewSettings::~ViewSettings() {
RequestOpenWindow::unsubscribe(this); RequestOpenWindow::unsubscribe(this);
EventImHexStartupFinished::unsubscribe(this);
} }
void ViewSettings::drawContent() { void ViewSettings::drawContent() {

View File

@ -81,6 +81,8 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
loadFonts(); loadFonts();
extractBundledFiles(); extractBundledFiles();
registerMainMenuEntries();
registerEventHandlers(); registerEventHandlers();
registerDataVisualizers(); registerDataVisualizers();
registerDataInspectorEntries(); registerDataInspectorEntries();
@ -114,7 +116,5 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
addToolbarItems(); addToolbarItems();
addGlobalUIItems(); addGlobalUIItems();
registerMainMenuEntries();
handleBorderlessWindowMode(); handleBorderlessWindowMode();
} }