feat: Implement paste behaviour popup when pasting over one-byte regions (#2004)
### Problem description This PR implements the feature request described in #1995, that describes a problem with the `Paste` vs `Paste all` commands. Users could be thrown off by having `Ctrl+V` act as a simple "Paste over selection", whereas it's generally accepted as a "Paste all". ### Implementation description <!-- Explain what you did to correct the problem --> This PR introduces a new setting, called "Paste behaviour" (under the "Hex Editor" category). This setting has three values: - `Paste over selection`: the current implementation for ImHex. Pastes only over the selection region, in this case pasting only one byte; - `Paste everything`: allows ImHex's `Paste` to behave like a `Paste all` when selecting one-byte regions; - `Ask me next time`: prompts the user for a choice of behaviour (default value). *Note: as users generally use `Paste all` when selecting one-byte regions, calling `Paste` when selecting over two or more bytes is not affected by this change, and will still behave like the usual `Paste` command.* When selecting a one-byte region, and calling the Paste command, users that have not defined a preferred behaviour in the settings will be prompted to choose one, using a brand new popup. The popup also allows the user to cancel, which will not change the settings' value, and will cancel the paste action altogether. ### Screenshots The new popup: ![image](https://github.com/user-attachments/assets/2b0fd532-d4e7-4209-9dd7-8a79278692ea) The new setting: ![image](https://github.com/user-attachments/assets/2644c35e-7332-422e-8fae-ae8ad0507126) ### Additional things I'm not very good with long descriptions, so I'm open to any suggestions regarding the text that is included in the popup! I do think however that we should keep a hint indicating that `Paste all` is always an option, which could solve the issue altogether for very new users. --------- Signed-off-by: BioTheWolff <47079795+BioTheWolff@users.noreply.github.com> Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
parent
bb99b9a0ef
commit
9375a60cd9
@ -80,6 +80,12 @@ namespace hex::plugin::builtin {
|
|||||||
void registerEvents();
|
void registerEvents();
|
||||||
void registerMenuItems();
|
void registerMenuItems();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method dedicated to handling paste behaviour when using the normal "Paste" option.
|
||||||
|
* Decides what to do based on user settings, or opens a popup to let them decide.
|
||||||
|
*/
|
||||||
|
void processPasteBehaviour(const Region &selection);
|
||||||
|
|
||||||
ui::HexEditor m_hexEditor;
|
ui::HexEditor m_hexEditor;
|
||||||
|
|
||||||
bool m_shouldOpenPopup = false;
|
bool m_shouldOpenPopup = false;
|
||||||
|
@ -490,6 +490,7 @@
|
|||||||
"hex.builtin.setting.hex_editor.char_padding": "Extra character cell padding",
|
"hex.builtin.setting.hex_editor.char_padding": "Extra character cell padding",
|
||||||
"hex.builtin.setting.hex_editor.highlight_color": "Selection highlight color",
|
"hex.builtin.setting.hex_editor.highlight_color": "Selection highlight color",
|
||||||
"hex.builtin.setting.hex_editor.pattern_parent_highlighting": "Highlight pattern parents on hover",
|
"hex.builtin.setting.hex_editor.pattern_parent_highlighting": "Highlight pattern parents on hover",
|
||||||
|
"hex.builtin.setting.hex_editor.paste_behaviour": "Single-Byte Paste behaviour",
|
||||||
"hex.builtin.setting.hex_editor.sync_scrolling": "Synchronize editor scroll position",
|
"hex.builtin.setting.hex_editor.sync_scrolling": "Synchronize editor scroll position",
|
||||||
"hex.builtin.setting.imhex": "ImHex",
|
"hex.builtin.setting.imhex": "ImHex",
|
||||||
"hex.builtin.setting.imhex.recent_files": "Recent Files",
|
"hex.builtin.setting.imhex.recent_files": "Recent Files",
|
||||||
@ -819,6 +820,11 @@
|
|||||||
"hex.builtin.view.hex_editor.menu.edit.open_in_new_provider": "Open selection view...",
|
"hex.builtin.view.hex_editor.menu.edit.open_in_new_provider": "Open selection view...",
|
||||||
"hex.builtin.view.hex_editor.menu.edit.paste": "Paste",
|
"hex.builtin.view.hex_editor.menu.edit.paste": "Paste",
|
||||||
"hex.builtin.view.hex_editor.menu.edit.paste_as": "Paste...",
|
"hex.builtin.view.hex_editor.menu.edit.paste_as": "Paste...",
|
||||||
|
"hex.builtin.view.hex_editor.menu.edit.paste.popup.title": "Choose paste behaviour",
|
||||||
|
"hex.builtin.view.hex_editor.menu.edit.paste.popup.description": "When pasting values into the Hex Editor View, ImHex will only overwrite the bytes that are currently selected. If only a single byte is selected however, this can feel counter-intuitive. Would you like to paste the entire content of your clipboard if only one byte is selected or should still only the selected bytes be replaced?",
|
||||||
|
"hex.builtin.view.hex_editor.menu.edit.paste.popup.hint": "Note: If you want to ensure pasting everything at all times, the 'Paste all' command is also available in the Edit Menu!",
|
||||||
|
"hex.builtin.view.hex_editor.menu.edit.paste.popup.button.everything": "Paste everything",
|
||||||
|
"hex.builtin.view.hex_editor.menu.edit.paste.popup.button.selection": "Paste only over selection",
|
||||||
"hex.builtin.view.hex_editor.menu.edit.paste_all": "Paste all",
|
"hex.builtin.view.hex_editor.menu.edit.paste_all": "Paste all",
|
||||||
"hex.builtin.view.hex_editor.menu.edit.paste_all_string": "Paste all as string",
|
"hex.builtin.view.hex_editor.menu.edit.paste_all_string": "Paste all as string",
|
||||||
"hex.builtin.view.hex_editor.menu.edit.remove": "Remove...",
|
"hex.builtin.view.hex_editor.menu.edit.remove": "Remove...",
|
||||||
|
@ -835,6 +835,12 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", true);
|
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", true);
|
||||||
|
|
||||||
|
std::vector<std::string> pasteBehaviourNames = { "Ask me next time", "Paste everything", "Paste over selection" };
|
||||||
|
std::vector<nlohmann::json> pasteBehaviourValues = { "none", "everything", "selection" };
|
||||||
|
ContentRegistry::Settings::add<Widgets::DropDown>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.paste_behaviour",
|
||||||
|
pasteBehaviourNames,
|
||||||
|
pasteBehaviourValues,
|
||||||
|
"none");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fonts */
|
/* Fonts */
|
||||||
|
@ -483,6 +483,47 @@ namespace hex::plugin::builtin {
|
|||||||
std::string m_input;
|
std::string m_input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PopupPasteBehaviour final : public ViewHexEditor::Popup {
|
||||||
|
public:
|
||||||
|
explicit PopupPasteBehaviour(const Region &selection, const auto &pasteCallback) : m_selection(), m_pasteCallback(pasteCallback) {
|
||||||
|
m_selection = Region { .address=selection.getStartAddress(), .size=selection.getSize() };
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(ViewHexEditor *editor) override {
|
||||||
|
const auto width = ImGui::GetWindowWidth();
|
||||||
|
|
||||||
|
ImGui::TextWrapped("%s", "hex.builtin.view.hex_editor.menu.edit.paste.popup.description"_lang.get());
|
||||||
|
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.paste.popup.hint"_lang);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.selection"_lang, ImVec2(width / 4, 0))) {
|
||||||
|
m_pasteCallback(m_selection, true);
|
||||||
|
editor->closePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("hex.builtin.view.hex_editor.menu.edit.paste.popup.button.everything"_lang, ImVec2(width / 4, 0))) {
|
||||||
|
m_pasteCallback(m_selection, false);
|
||||||
|
editor->closePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetCursorPosX() - (width / 6));
|
||||||
|
if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 6, 0))) {
|
||||||
|
// Cancel the action, without updating settings nor pasting.
|
||||||
|
editor->closePopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] UnlocalizedString getTitle() const override {
|
||||||
|
return "hex.builtin.view.hex_editor.menu.edit.paste.popup.title"_lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Region m_selection;
|
||||||
|
std::function<void(const Region &selection, bool selectionCheck)> m_pasteCallback;
|
||||||
|
};
|
||||||
|
|
||||||
/* Hex Editor */
|
/* Hex Editor */
|
||||||
|
|
||||||
ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name", ICON_VS_FILE_BINARY) {
|
ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name", ICON_VS_FILE_BINARY) {
|
||||||
@ -730,6 +771,36 @@ namespace hex::plugin::builtin {
|
|||||||
provider->write(selection.getStartAddress(), buffer.data(), size);
|
provider->write(selection.getStartAddress(), buffer.data(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewHexEditor::processPasteBehaviour(const Region &selection) {
|
||||||
|
if (selection.getSize() > 1) {
|
||||||
|
// Apply normal "paste over selection" behaviour when pasting over several bytes
|
||||||
|
pasteBytes(selection, true, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection is over one byte, we have to check the settings to decide the course of action
|
||||||
|
|
||||||
|
auto setting = ContentRegistry::Settings::read<std::string>(
|
||||||
|
"hex.builtin.setting.hex_editor",
|
||||||
|
"hex.builtin.setting.hex_editor.paste_behaviour",
|
||||||
|
"none");
|
||||||
|
|
||||||
|
if (setting == "everything")
|
||||||
|
pasteBytes(selection, false, false);
|
||||||
|
else if (setting == "selection")
|
||||||
|
pasteBytes(selection, true, false);
|
||||||
|
else
|
||||||
|
this->openPopup<PopupPasteBehaviour>(selection,
|
||||||
|
[](const Region &selection, const bool selectionCheck) {
|
||||||
|
ContentRegistry::Settings::write<std::string>(
|
||||||
|
"hex.builtin.setting.hex_editor",
|
||||||
|
"hex.builtin.setting.hex_editor.paste_behaviour",
|
||||||
|
selectionCheck ? "selection" : "everything");
|
||||||
|
pasteBytes(selection, selectionCheck, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static void copyString(const Region &selection) {
|
static void copyString(const Region &selection) {
|
||||||
auto provider = ImHexApi::Provider::get();
|
auto provider = ImHexApi::Provider::get();
|
||||||
if (provider == nullptr)
|
if (provider == nullptr)
|
||||||
@ -1190,8 +1261,8 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
/* Paste */
|
/* Paste */
|
||||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.paste" }, ICON_VS_OUTPUT, 1450, CurrentView + CTRLCMD + Keys::V,
|
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.paste" }, ICON_VS_OUTPUT, 1450, CurrentView + CTRLCMD + Keys::V,
|
||||||
[] {
|
[this] {
|
||||||
pasteBytes(ImHexApi::HexEditor::getSelection().value_or( ImHexApi::HexEditor::ProviderRegion(Region { 0, 0 }, ImHexApi::Provider::get())), true, false);
|
processPasteBehaviour(ImHexApi::HexEditor::getSelection().value_or( ImHexApi::HexEditor::ProviderRegion(Region { 0, 0 }, ImHexApi::Provider::get())));
|
||||||
},
|
},
|
||||||
ImHexApi::HexEditor::isSelectionValid,
|
ImHexApi::HexEditor::isSelectionValid,
|
||||||
this);
|
this);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user