diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index c91e693d..8efc1e0e 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -97,6 +97,7 @@ SET(cockatrice_SOURCES src/shortcutssettings.cpp src/sequenceEdit/sequenceedit.cpp src/sequenceEdit/shortcutstab.cpp + src/lineeditcompleter.cpp ${VERSION_STRING_CPP} ) diff --git a/cockatrice/src/lineeditcompleter.cpp b/cockatrice/src/lineeditcompleter.cpp new file mode 100644 index 00000000..6860295d --- /dev/null +++ b/cockatrice/src/lineeditcompleter.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lineeditcompleter.h" + +LineEditCompleter::LineEditCompleter(QWidget *parent) +: QLineEdit(parent) +{ +} + +void LineEditCompleter::focusOutEvent(QFocusEvent * e){ + QLineEdit::focusOutEvent(e); + if (c->popup()->isVisible()){ + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndex = textValue.length(); + int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; + int leftShift = qMin(lastIndex, lastWordStartIndex); + setText(textValue.left(leftShift)); + //Insert highlighted line from popup + insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); + //Set focus back to the textbox since tab was pressed + setFocus(); + } +} + +void LineEditCompleter::keyPressEvent(QKeyEvent * event) +{ + switch (event->key()){ + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Escape: + if (c->popup()->isVisible()){ + event->ignore(); + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndexof = qMax(0, textValue.lastIndexOf(" ")); + QString finalString = textValue.left(lastIndexof); + //Add a space if there's a word + if (finalString != "") + finalString += " "; + setText(finalString); + return; + } + break; + case Qt::Key_Space: + if (c->popup()->isVisible()){ + event->ignore(); + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndex = textValue.length(); + int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; + int leftShift = qMin(lastIndex, lastWordStartIndex); + setText(textValue.left(leftShift)); + //Insert highlighted line from popup + insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); + return; + } + break; + default: + break; + } + + QLineEdit::keyPressEvent(event); + QString textValue = text(); + //Wait until the first character after @ + if (!c || text().right(1).contains("@")) + return; + + //Set new completion prefix + c->setCompletionPrefix(cursorWord(text())); + if (c->completionPrefix().length() < 1){ + c->popup()->hide(); + return; + } + + //Draw completion box + QRect cr = cursorRect(); + cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width()); + c->complete(cr); + + //Select first item in the completion popup + QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel()); + c->popup()->setSelectionModel(sm); + sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect); + sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate); +} + +QString LineEditCompleter::cursorWord(const QString &line) const +{ + return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1, + cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1); +} + +void LineEditCompleter::insertCompletion(QString arg) +{ + QString s_arg = arg + " "; + setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1, + cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg)); +} + +void LineEditCompleter::setCompleter(QCompleter* completer) +{ + c = completer; + c->setWidget(this); + connect(c, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString))); +} + +void LineEditCompleter::setCompletionList(QStringList completionList) +{ + if (!c || c->popup()->isVisible()) + return; + + QStringListModel *model; + model = (QStringListModel*)(c->model()); + if (model == NULL) + model = new QStringListModel(); + model->setStringList(completionList); +} \ No newline at end of file diff --git a/cockatrice/src/lineeditcompleter.h b/cockatrice/src/lineeditcompleter.h new file mode 100644 index 00000000..673a78d2 --- /dev/null +++ b/cockatrice/src/lineeditcompleter.h @@ -0,0 +1,26 @@ +#ifndef LINEEDITCOMPLETER_H +#define LINEEDITCOMPLETER_H + +#include +#include +#include +#include + +class LineEditCompleter : public QLineEdit +{ + Q_OBJECT +private: + QString cursorWord(const QString& line) const; + QCompleter* c; +private slots: + void insertCompletion(QString); +protected: + void keyPressEvent(QKeyEvent * event); + void focusOutEvent(QFocusEvent * e); +public: + explicit LineEditCompleter(QWidget *parent = 0); + void setCompleter(QCompleter*); + void setCompletionList(QStringList); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 22f54831..53ba95c3 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "dlg_creategame.h" #include "tab_game.h" @@ -31,6 +33,7 @@ #include "settingscache.h" #include "carddatabase.h" #include "replay_timeline_widget.h" +#include "lineeditcompleter.h" #include #include "pending_command.h" @@ -463,8 +466,9 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); + connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged())); sayLabel = new QLabel; - sayEdit = new QLineEdit; + sayEdit = new LineEditCompleter; sayLabel->setBuddy(sayEdit); QHBoxLayout *hLayout = new QHBoxLayout; @@ -555,6 +559,17 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client for (int i = gameInfo.game_types_size() - 1; i >= 0; i--) gameTypes.append(roomGameTypes.find(gameInfo.game_types(i)).value()); + + completer = new QCompleter(autocompleteUserList, sayEdit); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setMaxVisibleItems(5); + + #if QT_VERSION >= 0x050000 + completer->setFilterMode(Qt::MatchStartsWith); + #endif + + sayEdit->setCompleter(completer); + actCompleterChanged(); } void TabGame::addMentionTag(QString value) { @@ -721,6 +736,9 @@ void TabGame::actLeaveGame() void TabGame::actSay() { + if (completer->popup()->isVisible()) + return; + if (!sayEdit->text().isEmpty()) { Command_GameSay cmd; cmd.set_message(sayEdit->text().toStdString()); @@ -779,11 +797,21 @@ void TabGame::actRotateViewCCW() scene->adjustPlayerRotation(1); } +void TabGame::actCompleterChanged() +{ + settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1); +} + Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info) { bool local = ((clients.size() > 1) || (playerId == localPlayerId)); Player *newPlayer = new Player(info, playerId, local, this); connect(newPlayer, SIGNAL(openDeckEditor(const DeckLoader *)), this, SIGNAL(openDeckEditor(const DeckLoader *))); + QString newPlayerName = "@" + newPlayer->getName(); + if (!autocompleteUserList.contains(newPlayerName)){ + autocompleteUserList << newPlayerName; + sayEdit->setCompletionList(autocompleteUserList); + } scene->addPlayer(newPlayer); connect(newPlayer, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *))); @@ -803,7 +831,6 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info) players.insert(playerId, newPlayer); emit playerAdded(newPlayer); - return newPlayer; } @@ -978,6 +1005,9 @@ void TabGame::eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, c void TabGame::eventSpectatorLeave(const Event_Leave & /*event*/, int eventPlayerId, const GameEventContext & /*context*/) { + QString playerName = "@" + QString::fromStdString(spectators.value(eventPlayerId).name()); + if (autocompleteUserList.removeOne(playerName)) + sayEdit->setCompletionList(autocompleteUserList); messageLog->logLeaveSpectator(QString::fromStdString(spectators.value(eventPlayerId).name())); playerListWidget->removePlayer(eventPlayerId); spectators.remove(eventPlayerId); @@ -992,6 +1022,11 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event, int /*e const ServerInfo_Player &playerInfo = event.player_list(i); const ServerInfo_PlayerProperties &prop = playerInfo.properties(); const int playerId = prop.player_id(); + QString playerName = "@" + QString::fromStdString(prop.user_info().name()); + if (!autocompleteUserList.contains(playerName)){ + autocompleteUserList << playerName; + sayEdit->setCompletionList(autocompleteUserList); + } if (prop.spectator()) { if (!spectators.contains(playerId)) { spectators.insert(playerId, prop.user_info()); @@ -1099,11 +1134,18 @@ void TabGame::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const Ga { const ServerInfo_PlayerProperties &playerInfo = event.player_properties(); const int playerId = playerInfo.player_id(); + QString playerName = QString::fromStdString(playerInfo.user_info().name()); + if (!autocompleteUserList.contains("@" + playerName)){ + autocompleteUserList << "@" + playerName; + sayEdit->setCompletionList(autocompleteUserList); + } + if (players.contains(playerId)) return; + if (playerInfo.spectator()) { spectators.insert(playerId, playerInfo.user_info()); - messageLog->logJoinSpectator(QString::fromStdString(playerInfo.user_info().name())); + messageLog->logJoinSpectator(playerName); } else { Player *newPlayer = addPlayer(playerId, playerInfo.user_info()); messageLog->logJoin(newPlayer); @@ -1118,6 +1160,10 @@ void TabGame::eventLeave(const Event_Leave & /*event*/, int eventPlayerId, const if (!player) return; + QString playerName = "@" + player->getName(); + if(autocompleteUserList.removeOne(playerName)) + sayEdit->setCompletionList(autocompleteUserList); + messageLog->logLeave(player); playerListWidget->removePlayer(eventPlayerId); players.remove(eventPlayerId); diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index 719d4dd5..5b13ffdd 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -3,6 +3,7 @@ #include #include +#include #include "tab.h" #include "pb/serverinfo_game.pb.h" @@ -54,6 +55,7 @@ class QHBoxLayout; class GameReplay; class ServerInfo_User; class PendingCommand; +class LineEditCompleter; class ToggleButton : public QPushButton { Q_OBJECT @@ -117,6 +119,8 @@ private: CardItem *activeCard; bool gameClosed; QStringList gameTypes; + QCompleter *completer; + QStringList autocompleteUserList; // Replay related members GameReplay *replay; @@ -131,7 +135,7 @@ private: QLabel *timeElapsedLabel; MessageLogWidget *messageLog; QLabel *sayLabel; - QLineEdit *sayEdit; + LineEditCompleter *sayEdit; PhasesToolbar *phasesToolbar; GameScene *scene; GameView *gameView; @@ -200,7 +204,10 @@ private slots: void addMentionTag(QString value); void commandFinished(const Response &response); + void refreshShortcuts(); + + void actCompleterChanged(); public: TabGame(TabSupervisor *_tabSupervisor, QList &_clients, const Event_GameJoined &event, const QMap &_roomGameTypes); TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay); diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index 45b6f281..bdf19192 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -13,12 +13,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -28,6 +22,7 @@ #include "gameselector.h" #include "settingscache.h" #include "main.h" +#include "lineeditcompleter.h" #include "get_pb_extension.h" #include "pb/room_commands.pb.h" @@ -61,7 +56,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged())); sayLabel = new QLabel; - sayEdit = new CustomLineEdit; + sayEdit = new LineEditCompleter; sayLabel->setBuddy(sayEdit); connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); @@ -256,7 +251,7 @@ void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event) userList->sortItems(); if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))){ autocompleteUserList << "@" + QString::fromStdString(event.user_info().name()); - sayEdit->updateCompleterModel(autocompleteUserList); + sayEdit->setCompletionList(autocompleteUserList); } } @@ -264,7 +259,7 @@ void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event) { userList->deleteUser(QString::fromStdString(event.name())); autocompleteUserList.removeOne("@" + QString::fromStdString(event.name())); - sayEdit->updateCompleterModel(autocompleteUserList); + sayEdit->setCompletionList(autocompleteUserList); } void TabRoom::processRoomSayEvent(const Event_RoomSay &event) @@ -301,125 +296,4 @@ PendingCommand *TabRoom::prepareRoomCommand(const ::google::protobuf::Message &c void TabRoom::sendRoomCommand(PendingCommand *pend) { client->sendCommand(pend); -} - -CustomLineEdit::CustomLineEdit(QWidget *parent) - : QLineEdit(parent) -{ -} - -void CustomLineEdit::focusOutEvent(QFocusEvent * e){ - QLineEdit::focusOutEvent(e); - if (c->popup()->isVisible()){ - //Remove Popup - c->popup()->hide(); - //Truncate the line to last space or whole string - QString textValue = text(); - int lastIndex = textValue.length(); - int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; - int leftShift = qMin(lastIndex, lastWordStartIndex); - setText(textValue.left(leftShift)); - //Insert highlighted line from popup - insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); - //Set focus back to the textbox since tab was pressed - setFocus(); - } -} - -void CustomLineEdit::keyPressEvent(QKeyEvent * event) -{ - switch (event->key()){ - case Qt::Key_Return: - case Qt::Key_Enter: - case Qt::Key_Escape: - if (c->popup()->isVisible()){ - event->ignore(); - //Remove Popup - c->popup()->hide(); - //Truncate the line to last space or whole string - QString textValue = text(); - int lastIndexof = textValue.lastIndexOf(" "); - QString finalString = textValue.left(lastIndexof); - //Add a space if there's a word - if (finalString != "") - finalString += " "; - setText(finalString); - return; - } - break; - case Qt::Key_Space: - if (c->popup()->isVisible()){ - event->ignore(); - //Remove Popup - c->popup()->hide(); - //Truncate the line to last space or whole string - QString textValue = text(); - int lastIndex = textValue.length(); - int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; - int leftShift = qMin(lastIndex, lastWordStartIndex); - setText(textValue.left(leftShift)); - //Insert highlighted line from popup - insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); - return; - } - break; - default: - break; - } - - QLineEdit::keyPressEvent(event); - //Wait until the first character after @ - if (!c || text().right(1).contains("@")) - return; - - //Set new completion prefix - c->setCompletionPrefix(cursorWord(text())); - if (c->completionPrefix().length() < 1){ - c->popup()->hide(); - return; - } - - //Draw completion box - QRect cr = cursorRect(); - cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width()); - c->complete(cr); - - //Select first item in the completion popup - QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel()); - c->popup()->setSelectionModel(sm); - sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect); - sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate); -} - -QString CustomLineEdit::cursorWord(const QString &line) const -{ - return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1, - cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1); -} - -void CustomLineEdit::insertCompletion(QString arg) -{ - QString s_arg = arg + " "; - setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1, - cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg)); -} - -void CustomLineEdit::setCompleter(QCompleter* completer) -{ - c = completer; - c->setWidget(this); - connect(c, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString))); -} - -void CustomLineEdit::updateCompleterModel(QStringList completionList) -{ - if (!c || c->popup()->isVisible()) - return; - - QStringListModel *model; - model = (QStringListModel*)(c->model()); - if (model == NULL) - model = new QStringListModel(); - QStringList updatedList = completionList; - model->setStringList(updatedList); -} +} \ No newline at end of file diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index d8201e08..41b26b75 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -2,6 +2,7 @@ #define TAB_ROOM_H #include "tab.h" +#include "lineeditcompleter.h" #include #include #include @@ -28,7 +29,7 @@ class GameSelector; class Response; class PendingCommand; class ServerInfo_User; -class CustomLineEdit; +class LineEditCompleter; class TabRoom : public Tab { Q_OBJECT @@ -43,7 +44,7 @@ private: UserList *userList; ChatView *chatView; QLabel *sayLabel; - CustomLineEdit *sayEdit; + LineEditCompleter *sayEdit; QGroupBox *chatGroupBox; QMenu *roomMenu; @@ -91,21 +92,4 @@ public: void sendRoomCommand(PendingCommand *pend); }; -class CustomLineEdit : public QLineEdit -{ - Q_OBJECT -private: - QString cursorWord(const QString& line) const; - QCompleter* c; -private slots: - void insertCompletion(QString); -protected: - void keyPressEvent(QKeyEvent * event); - void focusOutEvent(QFocusEvent * e); -public: - explicit CustomLineEdit(QWidget *parent = 0); - void setCompleter(QCompleter*); - void updateCompleterModel(QStringList); -}; - #endif