Skip to content

Commit

Permalink
Shell: Basic curses -> Qt key mapping, command line editing
Browse files Browse the repository at this point in the history
There is an opportunity here to share code between the text-mode
command line widget and its GUI counterpart, maybe a common base class
would be in order.
  • Loading branch information
skyjake committed Jan 24, 2013
1 parent 7e3cfc0 commit be2b1cf
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 18 deletions.
10 changes: 9 additions & 1 deletion doomsday/libdeng2/include/de/widgets/rootwidget.h
Expand Up @@ -72,9 +72,17 @@ class RootWidget : public Widget
*/
Widget *focus() const;

/**
* Propagates an event to the full tree of widgets (until it gets eaten).
*
* @param event Event to handle.
*
* @return @c true, if event was eaten.
*/
bool processEvent(Event const *event);

void initialize();
void draw();
bool handleEvent(Event const *event);

private:
struct Instance;
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libdeng2/src/widgets/rootwidget.cpp
Expand Up @@ -123,7 +123,7 @@ void RootWidget::draw()
Rule::markRulesValid(); // All done for this frame.
}

bool RootWidget::handleEvent(Event const *event)
bool RootWidget::processEvent(Event const *event)
{
return dispatchEvent(event, &Widget::handleEvent);
}
Expand Down
64 changes: 61 additions & 3 deletions doomsday/tools/shell/shell-text/src/commandlinewidget.cpp
Expand Up @@ -74,12 +74,70 @@ void CommandLineWidget::draw()

bool CommandLineWidget::handleEvent(Event const *event)
{
// There are only key press events.
DENG2_ASSERT(event->type() == Event::KeyPress);
KeyEvent const *ev = static_cast<KeyEvent const *>(event);

d->command += ev->key();
d->cursorPos++;
bool eaten = true;

// Insert text?
if(!ev->text().isEmpty())
{
d->command.insert(d->cursorPos++, ev->text());
}
else
{
// Control character.
eaten = handleControlKey(ev->key());
}

root().requestDraw();
return true;
return eaten;
}

bool CommandLineWidget::handleControlKey(int key)
{
switch(key)
{
case Qt::Key_Backspace:
if(d->command.size() > 0 && d->cursorPos > 0)
{
d->command.remove(--d->cursorPos, 1);
}
return true;

case Qt::Key_Delete:
if(d->command.size() > d->cursorPos)
{
d->command.remove(d->cursorPos, 1);
}
return true;

case Qt::Key_Left:
if(d->cursorPos > 0) --d->cursorPos;
return true;

case Qt::Key_Right:
if(d->cursorPos < d->command.size()) ++d->cursorPos;
return true;

case Qt::Key_Home:
d->cursorPos = 0;
return true;

case Qt::Key_End:
d->cursorPos = d->command.size();
return true;

case Qt::Key_K: // assuming Control mod
d->command = d->command.left(d->cursorPos);
return true;

default:
break;
}

return false;
}


3 changes: 3 additions & 0 deletions doomsday/tools/shell/shell-text/src/commandlinewidget.h
Expand Up @@ -36,8 +36,11 @@ class CommandLineWidget : public TextWidget
de::Vector2i cursorPosition();

void draw();

bool handleEvent(de::Event const *event);

bool handleControlKey(int key);

private:
struct Instance;
Instance *d;
Expand Down
67 changes: 57 additions & 10 deletions doomsday/tools/shell/shell-text/src/cursesapp.cpp
Expand Up @@ -111,7 +111,7 @@ struct CursesApp::Instance

refreshTimer = new QTimer(&self);
QObject::connect(refreshTimer, SIGNAL(timeout()), &self, SLOT(refresh()));
refreshTimer->start(1000 / 20);
refreshTimer->start(1000 / 30);
}

void initCursesState()
Expand Down Expand Up @@ -168,24 +168,71 @@ struct CursesApp::Instance
// We must redraw all characters since wclear was called.
rootWidget->rootCanvas().markDirty();
}
else if(key & KEY_CODE_YES)
else if((key & KEY_CODE_YES) || key < 0x20 || key == 0x7f)
{
//qDebug() << "Got code" << QString("0%1").arg(key, 0, 8).toAscii().constData();
int code = 0;
KeyEvent::Modifiers mods;

// There is a curses key code.
// Control keys.
switch(key)
{
case 0x1b: // Escape
self.quit(); // development only
return;

case 0x7f: // Delete
code = Qt::Key_Backspace;
break;

case 0x4: // Ctrl-D
code = Qt::Key_Delete;
break;

case KEY_BACKSPACE:
code = Qt::Key_Backspace;
break;

case KEY_BTAB: // back-tab
code = Qt::Key_Backtab;
break;

case KEY_LEFT:
code = Qt::Key_Left;
break;

case KEY_RIGHT:
code = Qt::Key_Right;
break;

case KEY_HOME:
case 0x1: // Ctrl-A
code = Qt::Key_Home;
break;

case KEY_END:
case 0x5: // Ctrl-E
code = Qt::Key_End;
break;

case 0xb: // Ctrl-K
code = Qt::Key_K;
mods = KeyEvent::Control;
break;

default:
// This key code is ignored.
if(key & KEY_CODE_YES)
qDebug() << "CURSES" << QString("0%1").arg(key, 0, 8).toAscii().constData();
else
// This key code is ignored.
qDebug() << QString("%1").arg(key, 0, 16);
break;
}
}
else if(key == 0x1b) // Escape
{
self.quit();

if(code)
{
KeyEvent ev(code, mods);
rootWidget->processEvent(&ev);
}
}
else
{
Expand Down Expand Up @@ -216,7 +263,7 @@ struct CursesApp::Instance
}

KeyEvent ev(keyStr);
rootWidget->handleEvent(&ev);
rootWidget->processEvent(&ev);
}
}

Expand Down
22 changes: 19 additions & 3 deletions doomsday/tools/shell/shell-text/src/keyevent.h
Expand Up @@ -21,19 +21,35 @@

#include <de/Event>
#include <de/String>
#include <QFlags>

/**
* Key press event generated when the user presses a key on the keyboard.
*/
class KeyEvent : public de::Event
{
public:
KeyEvent(de::String const &keyStr) : Event(KeyPress), _key(keyStr) {}
enum Modifier
{
None = 0x0,
Control = 0x1
};
Q_DECLARE_FLAGS(Modifiers, Modifier)

de::String key() const { return _key; }
public:
KeyEvent(de::String const &keyText) : Event(KeyPress), _text(keyText), _code(0) {}
KeyEvent(int keyCode, Modifiers mods = None) : Event(KeyPress), _code(keyCode), _modifiers(mods) {}

de::String text() const { return _text; }
int key() const { return _code; }
Modifiers modifiers() const { return _modifiers; }

private:
de::String _key;
de::String _text;
int _code;
Modifiers _modifiers;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(KeyEvent::Modifiers)

#endif // KEYEVENT_H

0 comments on commit be2b1cf

Please sign in to comment.