added undo/redo to EditableWidget

This commit is contained in:
thrust26 2020-11-05 22:16:58 +01:00
parent 7ef46b366d
commit b2fa192529
4 changed files with 115 additions and 10 deletions

View File

@ -27,6 +27,15 @@ EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
{ {
ASSERT_MAIN_THREAD; 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 #ifdef JOYSTICK_SUPPORT
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
{ {

View File

@ -338,6 +338,11 @@ class EventHandler
virtual void enableTextEvents(bool enable) = 0; virtual void enableTextEvents(bool enable) = 0;
#ifdef GUI_SUPPORT #ifdef GUI_SUPPORT
/**
Check for QWERTZ keyboard layout
*/
bool isQwertz() { return myQwertz; }
/** /**
Clipboard methods. Clipboard methods.
*/ */
@ -359,6 +364,11 @@ class EventHandler
// Global OSystem object // Global OSystem object
OSystem& myOSystem; OSystem& myOSystem;
#ifdef GUI_SUPPORT
// Keyboard layout
bool myQwertz{false};
#endif
/** /**
Methods which are called by derived classes to handle specific types Methods which are called by derived classes to handle specific types
of input. of input.

View File

@ -47,6 +47,9 @@ void EditableWidget::setText(const string& str, bool)
if(_filter(tolower(c))) if(_filter(tolower(c)))
_editString.push_back(c); _editString.push_back(c);
clearEdits();
doEdit();
_caretPos = int(_editString.size()); _caretPos = int(_editString.size());
_selectSize = 0; _selectSize = 0;
@ -78,12 +81,64 @@ void EditableWidget::lostFocusWidget()
_selectSize = 0; _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) bool EditableWidget::tryInsertChar(char c, int pos)
{ {
if(_filter(tolower(c))) if(_filter(tolower(c)))
{ {
_editString.insert(pos, 1, c); _editString.insert(pos, 1, c);
doEdit();
return true; return true;
} }
return false; return false;
@ -187,8 +242,12 @@ bool EditableWidget::handleControlKeys(StellaKey key, StellaMod mod)
sendCommand(EditableWidget::kChangedCmd, key, _id); sendCommand(EditableWidget::kChangedCmd, key, _id);
break; break;
case KBDK_Y:
case KBDK_Z: case KBDK_Z:
// TODO: undo if(key == KBDK_Y != instance().eventHandler().isQwertz())
dirty = redoEdit();
else
dirty = undoEdit();
break; break;
case KBDK_LEFT: 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; bool handled = false;
@ -480,12 +539,20 @@ bool EditableWidget::killChar(int direction)
_caretPos--; _caretPos--;
_editString.erase(_caretPos, 1); _editString.erase(_caretPos, 1);
handled = true; handled = true;
if(_selectSize < 0)
_selectSize++;
if(addEdit)
doEdit();
} }
} }
else if(direction == 1) // Delete next character (delete) else if(direction == 1) // Delete next character (delete)
{ {
_editString.erase(_caretPos, 1); _editString.erase(_caretPos, 1);
handled = true; handled = true;
if(_selectSize > 0)
_selectSize--;
if(addEdit)
doEdit();
} }
return handled; return handled;
@ -502,12 +569,13 @@ bool EditableWidget::killLine(int direction)
if(count > 0) if(count > 0)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
killChar(-1); killChar(-1, false);
handled = true; handled = true;
// remove selection for removed text // remove selection for removed text
if(_selectSize < 0) if(_selectSize < 0)
_selectSize = 0; _selectSize = 0;
doEdit();
} }
} }
else if(direction == 1) // erase from current position to end of line else if(direction == 1) // erase from current position to end of line
@ -516,12 +584,13 @@ bool EditableWidget::killLine(int direction)
if(count > 0) if(count > 0)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
killChar(+1); killChar(+1, false);
handled = true; handled = true;
// remove selection for removed text // remove selection for removed text
if(_selectSize > 0) if(_selectSize > 0)
_selectSize = 0; _selectSize = 0;
doEdit();
} }
} }
@ -551,12 +620,13 @@ bool EditableWidget::killLastWord()
if(count > 0) if(count > 0)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
killChar(-1); killChar(-1, false);
handled = true; handled = true;
// remove selection for removed word // remove selection for removed word
if(_selectSize < 0) if(_selectSize < 0)
_selectSize = std::min(_selectSize + count, 0); _selectSize = std::min(_selectSize + count, 0);
doEdit();
} }
return handled; return handled;
@ -649,7 +719,7 @@ int EditableWidget::selectEndPos()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EditableWidget::killSelectedText() bool EditableWidget::killSelectedText(bool addEdit)
{ {
if(_selectSize) if(_selectSize)
{ {
@ -660,6 +730,8 @@ bool EditableWidget::killSelectedText()
} }
_editString.erase(_caretPos, _selectSize); _editString.erase(_caretPos, _selectSize);
_selectSize = 0; _selectSize = 0;
if(addEdit)
doEdit();
return true; return true;
} }
return false; return false;
@ -703,7 +775,7 @@ bool EditableWidget::pasteSelectedText()
// retrieve the pasted text // retrieve the pasted text
instance().eventHandler().pasteText(pasted); instance().eventHandler().pasteText(pasted);
// remove the currently selected text // remove the currently selected text
killSelectedText(); killSelectedText(false);
// insert filtered paste text instead // insert filtered paste text instead
ostringstream buf; ostringstream buf;
bool lastOk = true; // only one filler char per invalid character (block) 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 // position cursor at the end of pasted text
_caretPos += int(buf.str().length()); _caretPos += int(buf.str().length());
return selected || !pasted.empty(); if(selected || !pasted.empty())
{
doEdit();
return true;
}
return false;
} }

View File

@ -19,6 +19,7 @@
#define EDITABLE_WIDGET_HXX #define EDITABLE_WIDGET_HXX
#include <functional> #include <functional>
#include <deque>
#include "Widget.hxx" #include "Widget.hxx"
#include "Rect.hxx" #include "Rect.hxx"
@ -89,18 +90,23 @@ class EditableWidget : public Widget, public CommandSender
bool handleControlKeys(StellaKey key, StellaMod mod); bool handleControlKeys(StellaKey key, StellaMod mod);
bool handleShiftKeys(StellaKey key); bool handleShiftKeys(StellaKey key);
bool handleNormalKeys(StellaKey key); bool handleNormalKeys(StellaKey key);
bool killChar(int direction); bool killChar(int direction, bool addEdit = true);
bool killLine(int direction); bool killLine(int direction);
bool killLastWord(); bool killLastWord();
bool moveWord(int direction, bool select); bool moveWord(int direction, bool select);
bool killSelectedText(); bool killSelectedText(bool addEdit = true);
int selectStartPos(); int selectStartPos();
int selectEndPos(); int selectEndPos();
// Clipboard // Clipboard
bool cutSelectedText(); bool cutSelectedText();
bool copySelectedText(); bool copySelectedText();
bool pasteSelectedText(); bool pasteSelectedText();
// Undo
void clearEdits();
void doEdit();
bool undoEdit();
bool redoEdit();
// Use the current TextFilter to insert a character into the // Use the current TextFilter to insert a character into the
// internal buffer // internal buffer
@ -109,6 +115,9 @@ class EditableWidget : public Widget, public CommandSender
private: private:
bool _editable{true}; bool _editable{true};
string _editString; string _editString;
std::deque<string> _editBuffer;
int _redoCount{0};
int _caretPos{0}; int _caretPos{0};
// Size of current selected text // Size of current selected text
// 0 = no selection // 0 = no selection