Skip to content

Commit

Permalink
impr: Convert all hex editor popups to floating, movable windows (#1658)
Browse files Browse the repository at this point in the history
### Problem description
In previous versions of ImHex, all tool windows were implemented as
static popups fixed in the upper left position of the hex view. This PR
refactors all tool popups to use floating windows that can be dragged
around by the user, or closed with a dedicated close button on the title
bar. These popup also support a stylable transparency when the user is
not hovering their mouse over the window.

### Implementation description
I rewrote the logic in `ViewHexEditor::drawPopup()` to use a custom
`ImGuiExt::BeginHoveringPopup` function for rendering the popup windows.
This new function is an almost exact replica of the built-in
`ImGui::BeginPopupModal`, except it does also displays the default
window title bar with a close button.

A second custom function, `ImGuiExt::PopupTitleBarButton` was also added
for rendering small icon-based buttons into the title bar of the parent
popup window. This new function was used to implement an optional
"Pinning" feature that individual popup implementations can specify. If
a window is pinned, it won't close automatically when its main action is
executed. For example, the "Select" button on the Select dialog will
close the popup by default, unless the window is pinned.

### Screenshots
Popup dialogs before:

![image](https://github.com/WerWolv/ImHex/assets/45818400/7c253181-8284-4076-a066-089403554f0f)

Popup dialogs after:


https://github.com/WerWolv/ImHex/assets/45818400/99d1a628-8ac1-40ac-9146-9062091bb0db



### Additional things
- When the user stops hovering their mouse over a popup window, it
becomes semi-transparent, making it easier to see the content behind it
- This PR also introduces the `styles.imhex.popup-alpha` style, making
the transparency effect configurable, including the ability to disable
the effect completely by setting `popup-alpha` to `1.0`.
- Fixed a bug that caused some popup windows to ignore the Enter and the
KeypadEnter keys. With this PR, all tool windows will execute their main
action when the user presses either one of the two Enter keys, and will
also close automatically unless the window is pinned.

### Possible changes and improvements
- Should the transparency effect be disabled if a window is pinned?
- Should the transparency factor be modifiable on the Settings/Interface
page?
- A keyboard shortcut could be added for quickly pinning / unpinning the
current window.
- Can the pin icon stay on the left, or should it be moved next to the
close button, with a similar circular background?

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
  • Loading branch information
SparkyTD and WerWolv committed May 10, 2024
1 parent 5f192d5 commit 973af46
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 51 deletions.
4 changes: 4 additions & 0 deletions lib/libimhex/include/hex/ui/imgui_imhex_extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ namespace ImGuiExt {

struct Styles {
float WindowBlur = 0.0F;
float PopupWindowAlpha = 0.0F; // Alpha used by Popup tool windows when the user is not hovering over them
} styles;
};

Expand Down Expand Up @@ -304,6 +305,9 @@ namespace ImGuiExt {
bool ToggleSwitch(const char *label, bool *v);
bool ToggleSwitch(const char *label, bool v);

bool PopupTitleBarButton(const char* label, bool p_enabled);
void PopupTitleBarText(const char* text);

template<typename T>
constexpr ImGuiDataType getImGuiDataType() {
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;
Expand Down
53 changes: 53 additions & 0 deletions lib/libimhex/source/ui/imgui_imhex_extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,59 @@ namespace ImGuiExt {
return ToggleSwitch(label, &v);
}

bool PopupTitleBarButton(const char* label, bool p_enabled)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = window->GetID(label);
const ImRect title_rect = window->TitleBarRect();
const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency.
const ImVec2 pos = window->DC.CursorPos;
const ImVec2 max_pos = pos + size;
const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y);

ImGui::PushClipRect(title_rect.Min, title_rect.Max, false);

// Check for item addition (similar to how clipping is handled in the original button functions).
bool is_clipped = !ItemAdd(bb, id);
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
if (is_clipped)
{
ImGui::PopClipRect();
return pressed;
}

// const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
// window->DrawList->AddCircleFilled(bb.GetCenter(), ImMax(2.0f, g.FontSize * 0.5f + 1.0f), bg_col);

// Draw the label in the center
ImU32 text_col = GetColorU32(p_enabled || hovered ? ImGuiCol_Text : ImGuiCol_TextDisabled);
window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, label);

// Return the button press state
ImGui::PopClipRect();
return pressed;
}

void PopupTitleBarText(const char* text) {
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
const ImRect title_rect = window->TitleBarRect();
const ImVec2 size(g.FontSize, g.FontSize); // Button size matches font size for aesthetic consistency.
const ImVec2 pos = window->DC.CursorPos;
const ImVec2 max_pos = pos + size;
const ImRect bb(pos.x, title_rect.Min.y, max_pos.x, title_rect.Max.y);

ImGui::PushClipRect(title_rect.Min, title_rect.Max, false);

// Draw the label in the center
ImU32 text_col = GetColorU32(ImGuiCol_Text);
window->DrawList->AddText(bb.GetCenter() - ImVec2(g.FontSize * 0.45F, g.FontSize * 0.5F), text_col, text);

// Return the button press state
ImGui::PopClipRect();
}
}

namespace ImGui {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ namespace hex::plugin::builtin {
TaskHolder m_searchTask;

void processInputString();

[[nodiscard]] UnlocalizedString getTitle() const override;
};

} // namespace hex::plugin::builtin
11 changes: 11 additions & 0 deletions plugins/builtin/include/content/views/view_hex_editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ namespace hex::plugin::builtin {
public:
virtual ~Popup() = default;
virtual void draw(ViewHexEditor *editor) = 0;

[[nodiscard]] virtual UnlocalizedString getTitle() const { return {}; }

[[nodiscard]] virtual bool canBePinned() const { return false; }
[[nodiscard]] bool isPinned() const { return m_isPinned; }
void setPinned(const bool pinned) { m_isPinned = pinned; }
private:
bool m_isPinned = false;
};

[[nodiscard]] bool isAnyPopupOpen() const {
Expand Down Expand Up @@ -71,6 +79,9 @@ namespace hex::plugin::builtin {
ui::HexEditor m_hexEditor;

bool m_shouldOpenPopup = false;
bool m_currentPopupHasHovered = false; // This flag prevents the popup from initially appearing with the transparency effect
bool m_currentPopupHover = false;
bool m_currentPopupDetached = false;
std::unique_ptr<Popup> m_currPopup;

PerProvider<std::optional<u64>> m_selectionStart, m_selectionEnd;
Expand Down
3 changes: 2 additions & 1 deletion plugins/builtin/romfs/themes/classic.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@
]
},
"imhex": {
"window-blur": 0.0
"window-blur": 0.0,
"popup-alpha": 0.65
}
}
}
3 changes: 2 additions & 1 deletion plugins/builtin/romfs/themes/dark.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@
]
},
"imhex": {
"window-blur": 0.0
"window-blur": 0.0,
"popup-alpha": 0.65
}
}
}
3 changes: 2 additions & 1 deletion plugins/builtin/romfs/themes/light.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@
]
},
"imhex": {
"window-blur": 0.0
"window-blur": 0.0,
"popup-alpha": 0.65
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ namespace hex::plugin::builtin {
}

void PopupFind::draw(ViewHexEditor *editor) {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.search"_lang);

auto lastMode = *s_searchMode;
if (ImGui::BeginTabBar("##find_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.search.hex"_lang)) {
Expand Down Expand Up @@ -302,4 +300,8 @@ namespace hex::plugin::builtin {
break;
}
}

[[nodiscard]] UnlocalizedString PopupFind::getTitle() const {
return "hex.builtin.view.hex_editor.menu.file.search";
}
}
1 change: 1 addition & 0 deletions plugins/builtin/source/content/themes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ namespace hex::plugin::builtin {
auto &style = ImGuiExt::GetCustomStyle();
const static ThemeManager::StyleMap ImHexStyleMap = {
{ "window-blur", { &style.WindowBlur, 0.0F, 1.0F, true } },
{ "popup-alpha", { &style.PopupWindowAlpha, 0.0F, 1.0F, false } },
};

ThemeManager::addStyleHandler("imhex", ImHexStyleMap);
Expand Down
Loading

0 comments on commit 973af46

Please sign in to comment.