Added text edit right click menu

This commit is contained in:
Yousef Khadadeh 2025-12-02 09:53:03 +03:00
parent f227e074f8
commit d57d2f4752
10 changed files with 84 additions and 115 deletions

View file

@ -1,7 +1,14 @@
#include "DrawingProgramEditToolBase.hpp"
#include "../MainProgram.hpp"
DrawingProgramEditToolBase::DrawingProgramEditToolBase(DrawingProgram& initDrawP):
drawP(initDrawP)
{}
bool DrawingProgramEditToolBase::right_click_popup_gui(const std::shared_ptr<DrawComponent>& comp, Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
t.paint_popup(popupPos);
return true;
}
DrawingProgramEditToolBase::~DrawingProgramEditToolBase() {}

View file

@ -13,6 +13,7 @@ class DrawingProgramEditToolBase {
virtual void commit_edit_updates(const std::shared_ptr<DrawComponent>& a, std::any& prevData) = 0;
virtual bool edit_update(const std::shared_ptr<DrawComponent>& a) = 0;
virtual bool edit_gui(const std::shared_ptr<DrawComponent>& a) = 0;
virtual bool right_click_popup_gui(const std::shared_ptr<DrawComponent>& comp, Vector2f popupPos);
virtual ~DrawingProgramEditToolBase();
protected:
DrawingProgram& drawP;

View file

@ -42,7 +42,10 @@ void EditTool::gui_toolbox() {
}
bool EditTool::right_click_popup_gui(Vector2f popupPos) {
return drawP.selection_action_menu(popupPos);
if(controls.isEditing)
return controls.compEditTool->right_click_popup_gui(controls.compToEdit.lock(), popupPos);
else
return drawP.selection_action_menu(popupPos);
}
void EditTool::add_point_handle(const HandleData& handle) {

View file

@ -152,6 +152,37 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
return oldInputChangedTextBox;
}
bool TextBoxEditTool::right_click_popup_gui(const std::shared_ptr<DrawComponent>& comp, Vector2f popupPos) {
Toolbar& t = drawP.world.main.toolbar;
bool shouldClose = false;
std::shared_ptr<DrawTextBox> a = std::static_pointer_cast<DrawTextBox>(comp);
t.gui.list_popup_menu("Text popup menu", popupPos, [&]() {
t.gui.text_label_light("Text menu");
InputManager& input = drawP.world.main.input;
if(t.gui.text_button_left_transparent("Paste", "Paste")) {
input.call_text_paste(true);
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Paste without formatting", "Paste without formatting")) {
input.call_text_paste(false);
shouldClose = true;
}
if(a->cursor->selectionBeginPos != a->cursor->selectionEndPos) {
if(t.gui.text_button_left_transparent("Copy", "Copy")) {
input.set_clipboard_plain_and_richtext_pair(a->textBox->process_copy(*a->cursor));
shouldClose = true;
}
if(t.gui.text_button_left_transparent("Cut", "Cut")) {
input.text.do_textbox_operation_with_undo([&]() {
input.set_clipboard_plain_and_richtext_pair(a->textBox->process_cut(*a->cursor));
});
shouldClose = true;
}
}
});
return !shouldClose;
}
void TextBoxEditTool::hold_undo_data(const std::string& undoName, const std::shared_ptr<DrawTextBox>& a) {
auto it = undoHeldData.find(undoName);
if(it != undoHeldData.end()) {
@ -286,7 +317,7 @@ bool TextBoxEditTool::edit_update(const std::shared_ptr<DrawComponent>& comp) {
bool collidesWithBox = a->collides_with_cam_coords(drawP.world.drawData.cam.c, mousePointCollection);
input.text.set_rich_text_box_input(a->textBox, a->cursor, currentMods);
input.text.set_rich_text_box_input(a->textBox, a->cursor, true, currentMods);
a->textBox->process_mouse_left_button(*a->cursor, a->get_mouse_pos(drawP), (drawP.controls.leftClick && collidesWithBox) ? drawP.world.main.input.mouse.leftClicks : 0, drawP.controls.leftClickHeld, input.key(InputManager::KEY_GENERIC_LSHIFT).held);
return true;

View file

@ -14,6 +14,7 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
virtual void commit_edit_updates(const std::shared_ptr<DrawComponent>& a, std::any& prevData) override;
virtual bool edit_update(const std::shared_ptr<DrawComponent>& a) override;
virtual bool edit_gui(const std::shared_ptr<DrawComponent>& a) override;
virtual bool right_click_popup_gui(const std::shared_ptr<DrawComponent>& comp, Vector2f popupPos) override;
private:
struct TextBoxEditToolAllData {
DrawTextBox::Data textboxData;

View file

@ -89,14 +89,7 @@ struct UpdateInputData {
bool down = false;
bool leftShift = false;
bool leftCtrl = false;
bool home = false;
bool del = false;
bool backspace = false;
bool enter = false;
bool selectAll = false;
bool copy = false;
bool paste = false;
bool cut = false;
bool escape = false;
} key;

View file

@ -48,26 +48,6 @@ InputManager::InputManager() {
defaultKeyAssignments[{0, SDLK_SPACE}] = KEY_HOLD_TO_PAN;
#ifdef __EMSCRIPTEN__
// Without this, SDL eats the CTRL-V event that initiates the paste event
// https://github.com/pthom/hello_imgui/issues/3#issuecomment-1564536870
EM_ASM({
window.addEventListener('keydown', function(event) {
if((event.ctrlKey || event.metaKey) && (event.key == 'v' || event.code == 'KeyV')) {
if(Module["ccall"]('is_text_input_happening', 'number', [], []) === 1)
event.stopImmediatePropagation();
}
}, true);
});
emscripten_browser_clipboard::paste([](std::string&& pasteData, void* callbackData){
InputManager* inMan = (InputManager*)callbackData;
inMan->clipboardPasteEventHappened = true;
inMan->clipboardPasteEventData["text/plain"] = pasteData;
inMan->process_text_paste();
}, this);
#endif
keyAssignments = defaultKeyAssignments;
}
@ -81,11 +61,12 @@ void InputManager::text_input_silence_everything() {
text.newInput.clear();
}
void InputManager::Text::set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap) {
void InputManager::Text::set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, bool isRichTextBox, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap) {
set_accepting_input();
newTextBox = nTextBox;
newCursor = nCursor;
newModMap = nModMap;
newIsRichTextBox = isRichTextBox;
}
void InputManager::Text::add_text_to_textbox(const std::string& inputText) {
@ -129,41 +110,11 @@ void InputManager::Text::do_textbox_operation_with_undo(const std::function<void
}
}
bool InputManager::get_clipboard_paste_happened() {
#ifdef __EMSCRIPTEN__
return clipboardPasteEventHappened;
#else
return key(KEY_TEXT_PASTE).repeat;
#endif
}
std::string InputManager::get_clipboard_str() {
#ifdef __EMSCRIPTEN__
auto it = clipboardPasteEventData.find("text/plain");
if(it == clipboardPasteEventData.end())
return "";
return it->second;
#else
std::string InputManager::get_clipboard_str_SDL() {
char* data = SDL_GetClipboardText();
std::string toRet(data);
SDL_free(data);
return toRet;
#endif
}
std::string InputManager::get_clipboard_data_for_mimetype(const std::string& mimeType) {
#ifdef __EMSCRIPTEN__
auto it = clipboardPasteEventData.find(mimeType);
if(it == clipboardPasteEventData.end())
return "";
return it->second;
#else
size_t datSize = 0;
void* dat = SDL_GetClipboardData(mimeType.c_str(), &datSize);
if(!dat)
return "";
return std::string(static_cast<char*>(dat), datSize);
#endif
}
void InputManager::set_clipboard_str(std::string_view s) {
@ -180,30 +131,6 @@ void InputManager::set_clipboard_plain_and_richtext_pair(const std::pair<std::st
lastCopiedRichText = plainAndRichtextPair.second;
}
void InputManager::set_clipboard_data(const std::unordered_map<std::string, std::string>& newClipboardData) {
SDL_ClearClipboardData();
clipboardData = newClipboardData;
std::vector<const char*> mimeTypes;
for(auto& [k, v] : clipboardData)
mimeTypes.emplace_back(k.c_str());
SDL_SetClipboardData(
[](void *userdata, const char *mime_type, size_t *size) {
std::unordered_map<std::string, std::string>& clipboardData = *static_cast<std::unordered_map<std::string, std::string>*>(userdata);
std::string mimeTypeStr(mime_type);
auto it = clipboardData.find(mimeTypeStr);
std::cout << "requesting: " << mime_type << std::endl;
if(it == clipboardData.end())
return static_cast<const void*>(nullptr);
*size = it->second.length();
return static_cast<const void*>(it->second.c_str());
},
[](void *userdata) {
},
static_cast<void*>(&clipboardData), mimeTypes.data(), mimeTypes.size());
}
std::string InputManager::key_assignment_to_str(const Vector2ui32& k) {
std::string toRet;
if(k.x() & SDL_KMOD_CTRL)
@ -375,7 +302,7 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
set_key_down(e, KEY_TEXT_PASTE);
if(text.textBox && key(KEY_TEXT_PASTE).repeat)
text.do_textbox_operation_with_undo([&]() {
process_text_paste();
call_text_paste(true);
});
break;
case SDLK_A:
@ -427,11 +354,23 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
set_key_down(e, f->second);
}
void InputManager::process_text_paste() {
void InputManager::call_text_paste(bool isRichTextPaste) {
text.isNextPasteRich = isRichTextPaste;
#ifdef __EMSCRIPTEN__
emscripten_browser_clipboard::paste([](std::string&& pasteData, void* callbackData){
InputManager* inMan = (InputManager*)callbackData;
std::string pData = pasteData;
inMan->process_text_paste(pData);
}, this);
#else
process_text_paste(get_clipboard_str_SDL());
#endif
}
void InputManager::process_text_paste(const std::string& plainClipboardStr) {
if(text.textBox) {
// Workaround for not being able to copy richtext to system clipboard, this should at least work within the application itself
std::string plainClipboardStr = get_clipboard_str();
if(lastCopiedRichText.has_value()) {
if(text.isRichTextBox && text.isNextPasteRich && lastCopiedRichText.has_value()) {
if(lastCopiedRichText.value().get_plain_text() == plainClipboardStr)
text.textBox->process_rich_text_input(*text.cursor, lastCopiedRichText.value());
else
@ -546,8 +485,12 @@ void InputManager::frame_reset(const Vector2i& windowSize) {
text.cursor = text.newCursor;
text.textBox = text.newTextBox;
text.modMap = text.newModMap;
text.isRichTextBox = text.newIsRichTextBox;
text.newCursor = nullptr;
text.newTextBox = nullptr;
text.newModMap = std::nullopt;
text.newIsRichTextBox = false;
text.newInput.clear();
droppedItems.clear();

View file

@ -89,17 +89,21 @@ struct InputManager {
void set_accepting_input();
bool get_accepting_input();
void set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap = std::nullopt);
void set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, bool isRichTextBox, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap = std::nullopt);
void add_text_to_textbox(const std::string& inputText);
void add_textbox_undo(const RichText::TextBox::Cursor& prevCursor, const RichText::TextData& prevRichText);
void do_textbox_operation_with_undo(const std::function<void()>& func);
private:
bool isNextPasteRich = false;
bool isRichTextBox;
std::shared_ptr<RichText::TextBox> textBox;
std::shared_ptr<RichText::TextBox::Cursor> cursor;
std::optional<RichText::TextStyleModifier::ModifierMap> modMap;
bool newIsRichTextBox;
std::shared_ptr<RichText::TextBox> newTextBox;
std::shared_ptr<RichText::TextBox::Cursor> newCursor;
std::optional<RichText::TextStyleModifier::ModifierMap> newModMap;
@ -201,22 +205,11 @@ struct InputManager {
void backend_key_up_update(const SDL_KeyboardEvent& e);
void backend_key_down_update(const SDL_KeyboardEvent& e);
bool get_clipboard_paste_happened();
std::string get_clipboard_str();
std::string get_clipboard_data_for_mimetype(const std::string& mimeType);
void set_clipboard_str(std::string_view s);
void set_clipboard_plain_and_richtext_pair(const std::pair<std::string, RichText::TextData>& plainAndRichtextPair);
void set_clipboard_data(const std::unordered_map<std::string, std::string>& newClipboardData);
void process_text_paste();
std::unordered_map<std::string, std::string> clipboardData;
#ifdef __EMSCRIPTEN__
bool clipboardPasteEventHappened = false;
std::unordered_map<std::string, std::string> clipboardPasteEventData;
#endif
std::string get_clipboard_str_SDL();
void call_text_paste(bool isRichTextPaste);
void process_text_paste(const std::string& plainClipboardStr);
std::optional<RichText::TextData> lastCopiedRichText;

View file

@ -1983,17 +1983,10 @@ void Toolbar::initialize_io_before_update() {
io->key.down = main.input.key(InputManager::KEY_TEXT_DOWN).repeat;
io->key.leftShift = main.input.key(InputManager::KEY_TEXT_SHIFT).held;
io->key.leftCtrl = main.input.key(InputManager::KEY_TEXT_CTRL).held;
io->key.home = main.input.key(InputManager::KEY_TEXT_HOME).repeat;
io->key.del = main.input.key(InputManager::KEY_TEXT_DELETE).repeat;
io->key.backspace = main.input.key(InputManager::KEY_TEXT_BACKSPACE).repeat;
io->key.enter = main.input.key(InputManager::KEY_TEXT_ENTER).repeat;
io->key.selectAll = main.input.key(InputManager::KEY_TEXT_SELECTALL).repeat;
io->key.copy = main.input.key(InputManager::KEY_TEXT_COPY).repeat;
io->key.paste = main.input.get_clipboard_paste_happened();
io->key.cut = main.input.key(InputManager::KEY_TEXT_CUT).repeat;
io->key.escape = main.input.key(InputManager::KEY_GENERIC_ESCAPE).repeat;
io->richTextBoxEdit = [&](std::shared_ptr<RichText::TextBox> t, std::shared_ptr<RichText::TextBox::Cursor> c) {
main.input.text.set_rich_text_box_input(t, c);
main.input.text.set_rich_text_box_input(t, c, false);
};
if(io->acceptingTextInput)