From b2fa19252925612eb65454359c1bf0d03e4b9dcf Mon Sep 17 00:00:00 2001 From: thrust26 Date: Thu, 5 Nov 2020 22:16:58 +0100 Subject: [PATCH] added undo/redo to EditableWidget --- src/common/EventHandlerSDL2.cxx | 9 ++++ src/emucore/EventHandler.hxx | 10 ++++ src/gui/EditableWidget.cxx | 93 ++++++++++++++++++++++++++++++--- src/gui/EditableWidget.hxx | 13 ++++- 4 files changed, 115 insertions(+), 10 deletions(-) diff --git a/src/common/EventHandlerSDL2.cxx b/src/common/EventHandlerSDL2.cxx index 45d31ce9e..e5900cfba 100644 --- a/src/common/EventHandlerSDL2.cxx +++ b/src/common/EventHandlerSDL2.cxx @@ -27,6 +27,15 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem) { ASSERT_MAIN_THREAD; +#ifdef GUI_SUPPORT + { + ostringstream buf; + myQwertz = int('y') == int(SDL_GetKeyFromScancode(SDL_Scancode(KBDK_Z))); + buf << "Keyboard: " << (myQwertz ? "QWERTZ" : "QWERTY"); + Logger::debug(buf.str()); + } +#endif + #ifdef JOYSTICK_SUPPORT if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index 8649bd9e0..3833f7bec 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -338,6 +338,11 @@ class EventHandler virtual void enableTextEvents(bool enable) = 0; #ifdef GUI_SUPPORT + /** + Check for QWERTZ keyboard layout + */ + bool isQwertz() { return myQwertz; } + /** Clipboard methods. */ @@ -359,6 +364,11 @@ class EventHandler // Global OSystem object OSystem& myOSystem; + #ifdef GUI_SUPPORT + // Keyboard layout + bool myQwertz{false}; + #endif + /** Methods which are called by derived classes to handle specific types of input. diff --git a/src/gui/EditableWidget.cxx b/src/gui/EditableWidget.cxx index f58da8c7f..76b592516 100644 --- a/src/gui/EditableWidget.cxx +++ b/src/gui/EditableWidget.cxx @@ -47,6 +47,9 @@ void EditableWidget::setText(const string& str, bool) if(_filter(tolower(c))) _editString.push_back(c); + clearEdits(); + doEdit(); + _caretPos = int(_editString.size()); _selectSize = 0; @@ -78,12 +81,64 @@ void EditableWidget::lostFocusWidget() _selectSize = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::clearEdits() +{ + _editBuffer.clear(); + _redoCount = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::doEdit() +{ + constexpr size_t UNDO_SIZE = 100; + + // clear redos + for(; _redoCount; _redoCount--) + _editBuffer.pop_back(); + + if(_editBuffer.size() == UNDO_SIZE) + _editBuffer.pop_front(); + _editBuffer.push_back(_editString); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::undoEdit() +{ + if(_editBuffer.size() - _redoCount - 1) + { + _redoCount++; + _editString = _editBuffer[_editBuffer.size() - _redoCount - 1]; + _caretPos = int(_editString.size()); // TODO: put at last difference + _selectSize = 0; + + return true; + } + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::redoEdit() +{ + if(_redoCount) + { + _redoCount--; + _editString = _editBuffer[_editBuffer.size() - _redoCount - 1]; + _caretPos = int(_editString.size()); // TODO: put at last difference + _selectSize = 0; + + return true; + } + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::tryInsertChar(char c, int pos) { if(_filter(tolower(c))) { _editString.insert(pos, 1, c); + doEdit(); return true; } return false; @@ -187,8 +242,12 @@ bool EditableWidget::handleControlKeys(StellaKey key, StellaMod mod) sendCommand(EditableWidget::kChangedCmd, key, _id); break; + case KBDK_Y: case KBDK_Z: - // TODO: undo + if(key == KBDK_Y != instance().eventHandler().isQwertz()) + dirty = redoEdit(); + else + dirty = undoEdit(); break; case KBDK_LEFT: @@ -469,7 +528,7 @@ int EditableWidget::scrollOffset() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EditableWidget::killChar(int direction) +bool EditableWidget::killChar(int direction, bool addEdit) { bool handled = false; @@ -480,12 +539,20 @@ bool EditableWidget::killChar(int direction) _caretPos--; _editString.erase(_caretPos, 1); handled = true; + if(_selectSize < 0) + _selectSize++; + if(addEdit) + doEdit(); } } else if(direction == 1) // Delete next character (delete) { _editString.erase(_caretPos, 1); handled = true; + if(_selectSize > 0) + _selectSize--; + if(addEdit) + doEdit(); } return handled; @@ -502,12 +569,13 @@ bool EditableWidget::killLine(int direction) if(count > 0) { for (int i = 0; i < count; i++) - killChar(-1); + killChar(-1, false); handled = true; // remove selection for removed text if(_selectSize < 0) _selectSize = 0; + doEdit(); } } else if(direction == 1) // erase from current position to end of line @@ -516,12 +584,13 @@ bool EditableWidget::killLine(int direction) if(count > 0) { for (int i = 0; i < count; i++) - killChar(+1); + killChar(+1, false); handled = true; // remove selection for removed text if(_selectSize > 0) _selectSize = 0; + doEdit(); } } @@ -551,12 +620,13 @@ bool EditableWidget::killLastWord() if(count > 0) { for (int i = 0; i < count; i++) - killChar(-1); + killChar(-1, false); handled = true; // remove selection for removed word if(_selectSize < 0) _selectSize = std::min(_selectSize + count, 0); + doEdit(); } return handled; @@ -649,7 +719,7 @@ int EditableWidget::selectEndPos() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EditableWidget::killSelectedText() +bool EditableWidget::killSelectedText(bool addEdit) { if(_selectSize) { @@ -660,6 +730,8 @@ bool EditableWidget::killSelectedText() } _editString.erase(_caretPos, _selectSize); _selectSize = 0; + if(addEdit) + doEdit(); return true; } return false; @@ -703,7 +775,7 @@ bool EditableWidget::pasteSelectedText() // retrieve the pasted text instance().eventHandler().pasteText(pasted); // remove the currently selected text - killSelectedText(); + killSelectedText(false); // insert filtered paste text instead ostringstream buf; bool lastOk = true; // only one filler char per invalid character (block) @@ -725,5 +797,10 @@ bool EditableWidget::pasteSelectedText() // position cursor at the end of pasted text _caretPos += int(buf.str().length()); - return selected || !pasted.empty(); + if(selected || !pasted.empty()) + { + doEdit(); + return true; + } + return false; } diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index f6ef9dd9e..4d5ff71dc 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -19,6 +19,7 @@ #define EDITABLE_WIDGET_HXX #include +#include #include "Widget.hxx" #include "Rect.hxx" @@ -89,18 +90,23 @@ class EditableWidget : public Widget, public CommandSender bool handleControlKeys(StellaKey key, StellaMod mod); bool handleShiftKeys(StellaKey key); bool handleNormalKeys(StellaKey key); - bool killChar(int direction); + bool killChar(int direction, bool addEdit = true); bool killLine(int direction); bool killLastWord(); bool moveWord(int direction, bool select); - bool killSelectedText(); + bool killSelectedText(bool addEdit = true); int selectStartPos(); int selectEndPos(); // Clipboard bool cutSelectedText(); bool copySelectedText(); bool pasteSelectedText(); + // Undo + void clearEdits(); + void doEdit(); + bool undoEdit(); + bool redoEdit(); // Use the current TextFilter to insert a character into the // internal buffer @@ -109,6 +115,9 @@ class EditableWidget : public Widget, public CommandSender private: bool _editable{true}; string _editString; + + std::deque _editBuffer; + int _redoCount{0}; int _caretPos{0}; // Size of current selected text // 0 = no selection