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;
#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)
{

View File

@ -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.

View File

@ -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;
}

View File

@ -19,6 +19,7 @@
#define EDITABLE_WIDGET_HXX
#include <functional>
#include <deque>
#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<string> _editBuffer;
int _redoCount{0};
int _caretPos{0};
// Size of current selected text
// 0 = no selection