# HG changeset patch # User sheepluva # Date 1318983027 -7200 # Node ID 1d98752c1fbad08f54656551fb9d2bdeecec4d40 # Parent 0b92341adb6a6661d7f2a138c245ffc4161194ff frontend chat input history, use arrow keys UP/DOWN diff -r 0b92341adb6a -r 1d98752c1fba QTfrontend/ui/widget/SmartLineEdit.cpp --- a/QTfrontend/ui/widget/SmartLineEdit.cpp Tue Oct 18 15:34:40 2011 +0200 +++ b/QTfrontend/ui/widget/SmartLineEdit.cpp Wed Oct 19 02:10:27 2011 +0200 @@ -21,14 +21,19 @@ #include "SmartLineEdit.h" -SmartLineEdit::SmartLineEdit(QWidget * parent) +SmartLineEdit::SmartLineEdit(QWidget * parent, int maxHistorySize) : QLineEdit(parent) { + m_curHistEntryIdx = 0; + m_maxHistorySize = maxHistorySize; + m_whitespace = QRegExp("\\s"); m_cmds = new QStringList(); m_nicks = new QStringList(); + m_history = new QStringList(); + resetAutoCompletionStatus(); // reset autocompletion status when cursor is moved or content is changed @@ -41,58 +46,140 @@ void SmartLineEdit::addCommands(const QStringList & commands) { - m_mutex.lock(); + m_keywordMutex.lock(); m_cmds->append(commands); - m_mutex.unlock(); + m_keywordMutex.unlock(); } void SmartLineEdit::removeCommands(const QStringList & commands) { - m_mutex.lock(); + m_keywordMutex.lock(); foreach (const QString & cmd, commands) { m_cmds->removeAll(cmd); } - m_mutex.unlock(); + m_keywordMutex.unlock(); } void SmartLineEdit::addNickname(const QString & name) { - m_mutex.lock(); + m_keywordMutex.lock(); m_nicks->append(name); - m_mutex.unlock(); + m_keywordMutex.unlock(); } void SmartLineEdit::removeNickname(const QString & name) { - m_mutex.lock(); + m_keywordMutex.lock(); m_nicks->removeAll(name); - m_mutex.unlock(); + m_keywordMutex.unlock(); +} + +void SmartLineEdit::rememberCurrentText() +{ + m_historyMutex.lock(); + + rememberCurrentTextUnsynced(); + + m_historyMutex.unlock(); +} + +void SmartLineEdit::rememberCurrentTextUnsynced() +{ + QString newEntry = text(); + + // don't store whitespace-only/empty text + if (newEntry.trimmed().isEmpty()) + return; + + m_history->removeOne(newEntry); // no duplicates please + m_history->append(newEntry); + + // do not keep more entries than allowed + if (m_history->size() > m_maxHistorySize) + m_history->removeFirst(); + + // we're looking at the latest entry + m_curHistEntryIdx = m_history->size() - 1; +} + +void SmartLineEdit::clear() +{ + m_historyMutex.lock(); + + QLineEdit::clear(); + m_curHistEntryIdx = m_history->size(); + + m_historyMutex.unlock(); } void SmartLineEdit::forgetEverything() { - m_mutex.lock(); + // forget keywords + m_keywordMutex.lock(); m_cmds->clear(); m_nicks->clear(); - m_mutex.unlock(); + m_keywordMutex.unlock(); + + // forget history + m_historyMutex.lock(); + + m_history->clear(); + m_curHistEntryIdx = 0; + + m_historyMutex.unlock(); resetAutoCompletionStatus(); } +void SmartLineEdit::navigateHistory(bool isGoingUp) +{ + m_historyMutex.lock(); + + // save possible changes to new entry + if ((m_curHistEntryIdx >= m_history->size() || + (text() != m_history->at(m_curHistEntryIdx)))) + { + rememberCurrentTextUnsynced(); + } + + if (isGoingUp) + m_curHistEntryIdx--; + else + m_curHistEntryIdx++; + + // if Idx higher than valid range + if (m_curHistEntryIdx >= m_history->size()) + { + QLineEdit::clear(); + m_curHistEntryIdx = m_history->size(); + } + // if Idx in valid range + else if (m_curHistEntryIdx >= 0) + { + setText(m_history->at(m_curHistEntryIdx)); + } + // if Idx below 0 + else + m_curHistEntryIdx = 0; + + + m_historyMutex.unlock(); +} + bool SmartLineEdit::event(QEvent * event) { // we only want special treatment for key press events @@ -121,11 +208,29 @@ autoComplete(); event->accept(); } - // clear contents on pressed ESC - else if ((event->key() == Qt::Key_Escape) && - (event->modifiers() == Qt::NoModifier) - ) - clear(); + // clear contents on pressed ESC, navigate history with arrow keys + else if (event->modifiers() == Qt::NoModifier) + switch (key) + { + case Qt::Key_Escape: + clear(); + event->accept(); + break; + + case Qt::Key_Up: + navigateHistory(true); + event->accept(); + break; + + case Qt::Key_Down: + navigateHistory(false); + event->accept(); + break; + + default: + QLineEdit::keyPressEvent(event); + break; + } // otherwise forward keys to parent else QLineEdit::keyPressEvent(event); @@ -152,10 +257,10 @@ } else { - m_mutex.lock(); + m_keywordMutex.lock(); m_cmds->sort(); m_nicks->sort(); - m_mutex.unlock(); + m_keywordMutex.unlock(); int cp = cursorPosition(); @@ -190,7 +295,7 @@ } - m_mutex.lock(); + m_keywordMutex.lock(); if (isFirstWord) { @@ -229,7 +334,7 @@ } } - m_mutex.unlock(); + m_keywordMutex.unlock(); // we found a single match? if (!match.isEmpty()) diff -r 0b92341adb6a -r 1d98752c1fba QTfrontend/ui/widget/SmartLineEdit.h --- a/QTfrontend/ui/widget/SmartLineEdit.h Tue Oct 18 15:34:40 2011 +0200 +++ b/QTfrontend/ui/widget/SmartLineEdit.h Wed Oct 19 02:10:27 2011 +0200 @@ -33,11 +33,15 @@ class QLineEdit; /** - * A modification of QLineEdit that will attempt to auto-complete the current - * word with cursor when the TAB key is pressed. - * Additionally it will delete its contents when ESC is pressed. - * A Keyword can either be a command (if first word) or - * a nickname (completed if any word) + * A modification of QLineEdit that features: + * + Auto-completion for word under cursor when the TAB key is pressed. + * + ESC key clears text. + * + History of previous contents, re-selectable using the arrow keys. + * + * Note: + * * A Keyword can either be a command (if first word) or + * a nickname (completed regardless of position in text). + * * Public methods for accessing keywords and history are thread-safe. * @author sheepluva * @since 0.9.17 */ @@ -48,8 +52,10 @@ public: /** * Class constructor. + * @param parent parent QWidget. + * @param maxHistorySize maximum amount of history entries kept. */ - SmartLineEdit(QWidget * parent = 0); + SmartLineEdit(QWidget * parent = 0, int maxHistorySize = 64); /** * Adds commands to the auto-completion feature. @@ -64,6 +70,11 @@ void addNickname(const QString & nickname); /** + * Appends current text to history. + */ + void rememberCurrentText(); + + /** * Removes commands from the auto-completion feature. * @param commands list of commands to be removed. */ @@ -81,6 +92,13 @@ void forgetEverything(); +public slots: + /** + * Clears the contents. + */ + void clear(); + + protected: /** * Overrides method of parent class. @@ -94,9 +112,10 @@ /** * Overrides method of parent class. * Autocompletes if TAB is reported as pressed key in the key event, - * otherwise keys except for ESC (with no modifiers) + * otherwise keys except for ESC and Up/Down (with no modifiers) * are forwarded to parent method. * ESC leads to the contents being cleared. + * Arrow keys are used for navigating the history. * @param event the key event. */ virtual void keyPressEvent(QKeyEvent * event); @@ -105,9 +124,14 @@ private: QRegExp m_whitespace; // regexp that matches a whitespace + int m_maxHistorySize; // the maximum allowed size for the history + int m_curHistEntryIdx; // the index of the currently used entry or -1 + QStringList * m_cmds; // list of recognized commands QStringList * m_nicks; // list of recognized nicknames + QStringList * m_history; // history of previous inputs + // these variables contain information about the last replacement // they get reset whenever cursor is moved or text is changed @@ -116,13 +140,27 @@ QString m_prefix; // prefix of the text replacement this widget just did QString m_postfix; // postfix of the text replacement this widget just did - QMutex m_mutex; // make all the QStringList action thread-safe + QMutex m_keywordMutex; // make keyword QStringList action thread-safe + QMutex m_historyMutex; // make history QStringList action thread-safe /** * Autocompletes the contents based on the known commands and/or names. */ void autoComplete(); + /** + * Navigates content history in the desired direction. + * Note: no wrap-around on purpose (so that holding down/up will get the + * the user to the respective end rather than into an endless cycle :P) + * @param isGoingUp true: next older entry, false: next more recent entry. + */ + void navigateHistory(bool isGoingUp); + + /** + * Appends current text to history, without Mutex. + */ + void rememberCurrentTextUnsynced(); + private slots: /** diff -r 0b92341adb6a -r 1d98752c1fba QTfrontend/ui/widget/chatwidget.cpp --- a/QTfrontend/ui/widget/chatwidget.cpp Tue Oct 18 15:34:40 2011 +0200 +++ b/QTfrontend/ui/widget/chatwidget.cpp Wed Oct 19 02:10:27 2011 +0200 @@ -334,6 +334,7 @@ void HWChatWidget::returnPressed() { QStringList lines = chatEditLine->text().split('\n'); + chatEditLine->rememberCurrentText(); chatEditLine->clear(); foreach (const QString &line, lines) emit chatLine(line);