diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index f800928e3..82d99b605 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -1291,13 +1291,13 @@ void TabGame::createMessageDock(bool bReplay) connect(this, &TabGame::chatMessageSent, game->getGameEventHandler(), &GameEventHandler::handleChatMessageSent); mentionModel = new QStringListModel(autocompleteUserList, sayEdit); mentionCompleter = createMentionCompleter(mentionModel, sayEdit); - sayEdit->addCompleter(mentionCompleter, "@"); + sayEdit->addCompleter(mentionCompleter, CompleterTrigger::Mention); auto *cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, sayEdit); auto *displayModel = new CardDatabaseDisplayModel(sayEdit); displayModel->setSourceModel(cardDatabaseModel); const CardCompleterSetup cardSetup = createCardCompleter(displayModel, sayEdit); - sayEdit->addCompleter(cardSetup.completer, "[["); + sayEdit->addCompleter(cardSetup.completer, CompleterTrigger::Card); connect(sayEdit, &LineEditCompleter::cardPartialChanged, this, [this, cardSetup](const QString &text) { cardSetup.searchModel->updateSearchResults(text); diff --git a/cockatrice/src/interface/widgets/tabs/tab_room.cpp b/cockatrice/src/interface/widgets/tabs/tab_room.cpp index a703fe0e9..705266b1d 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_room.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_room.cpp @@ -147,13 +147,13 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, mentionModel = new QStringListModel(autocompleteUserList, sayEdit); mentionCompleter = createMentionCompleter(mentionModel, sayEdit); - sayEdit->addCompleter(mentionCompleter, "@"); + sayEdit->addCompleter(mentionCompleter, CompleterTrigger::Mention); auto *cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, sayEdit); auto *displayModel = new CardDatabaseDisplayModel(sayEdit); displayModel->setSourceModel(cardDatabaseModel); const CardCompleterSetup cardSetup = createCardCompleter(displayModel, sayEdit); - sayEdit->addCompleter(cardSetup.completer, "[["); + sayEdit->addCompleter(cardSetup.completer, CompleterTrigger::Card); connect(sayEdit, &LineEditCompleter::cardPartialChanged, this, [this, cardSetup](const QString &text) { cardSetup.searchModel->updateSearchResults(text); diff --git a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp index eace93801..6b1d2c255 100644 --- a/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp +++ b/cockatrice/src/interface/widgets/utility/card_completer_delegate.cpp @@ -25,7 +25,7 @@ struct ManaColor QColor text; }; -ManaColor manaColour(QChar symbol) +ManaColor manaColor(QChar symbol) { switch (symbol.unicode()) { case 'W': @@ -74,7 +74,7 @@ QColor CardCompleterDelegate::accentForColors(const QString &colors) return QColor(100, 115, 135); } - return manaColour(*seen.begin()).fill; + return manaColor(*seen.begin()).fill; } // --------------------------------------------------------------------------- @@ -144,7 +144,7 @@ void CardCompleterDelegate::drawManaSymbol(QPainter *p, QPoint centre, const QSt const QString label = isNumeric ? QString::number(numVal) : symbol; const ManaColor mc = - (symbol.length() == 1 && QString("WUBRG").contains(symbol)) ? manaColour(symbol[0]) : manaColour(QChar('X')); + (symbol.length() == 1 && QString("WUBRG").contains(symbol)) ? manaColor(symbol[0]) : manaColor(QChar('X')); QPainterPath circle; circle.addEllipse(pip); diff --git a/cockatrice/src/interface/widgets/utility/line_edit_completer.cpp b/cockatrice/src/interface/widgets/utility/line_edit_completer.cpp index 05738cb71..3a678029d 100644 --- a/cockatrice/src/interface/widgets/utility/line_edit_completer.cpp +++ b/cockatrice/src/interface/widgets/utility/line_edit_completer.cpp @@ -8,7 +8,7 @@ LineEditCompleter::LineEditCompleter(QWidget *parent) : LineEditUnfocusable(pare { } -void LineEditCompleter::addCompleter(QCompleter *c, const QString &trigger) +void LineEditCompleter::addCompleter(QCompleter *c, CompleterTrigger trigger) { c->setWidget(this); c->setCompletionMode(QCompleter::PopupCompletion); @@ -40,7 +40,14 @@ void LineEditCompleter::focusOutEvent(QFocusEvent *e) { LineEditUnfocusable::focusOutEvent(e); - // Commit the highlighted completion so that tabbing away still applies it + // Only commit the highlighted completion when focus moves away via Tab. + // Other focus losses (e.g. the unfocus shortcut / Escape) must simply close + // the popup without inserting anything. + if (e->reason() != Qt::TabFocusReason) { + hideCompleterPopups(); + return; + } + for (auto &info : completers) { if (!info.completer->popup()->isVisible()) { continue; @@ -71,22 +78,32 @@ void LineEditCompleter::keyPressEvent(QKeyEvent *event) QString prefix; for (auto &info : completers) { - if (info.trigger == "@") { - int triggerPos = textValue.lastIndexOf("@", cursorPos - 1); - if (triggerPos != -1 && (triggerPos == 0 || textValue[triggerPos - 1].isSpace())) { - active = &info; - prefix = textValue.mid(triggerPos + 1, cursorPos - (triggerPos + 1)); + bool triggered = false; + switch (info.trigger) { + case CompleterTrigger::Mention: { + int triggerPos = textValue.lastIndexOf("@", cursorPos - 1); + if (triggerPos != -1 && (triggerPos == 0 || textValue[triggerPos - 1].isSpace())) { + triggered = true; + // Keep the "@" so the prefix matches the "@"-prefixed mention model entries. + prefix = textValue.mid(triggerPos, cursorPos - triggerPos); + } break; } - } else if (info.trigger == "[[") { - int triggerPos = textValue.lastIndexOf("[[", cursorPos - 1); - int closePos = textValue.indexOf("]]", triggerPos + 2); - if (triggerPos != -1 && (closePos == -1 || closePos >= cursorPos)) { - active = &info; - prefix = textValue.mid(triggerPos + 2, cursorPos - (triggerPos + 2)); + case CompleterTrigger::Card: { + int triggerPos = textValue.lastIndexOf("[[", cursorPos - 1); + int closePos = textValue.indexOf("]]", triggerPos + 2); + if (triggerPos != -1 && (closePos == -1 || closePos >= cursorPos)) { + triggered = true; + prefix = textValue.mid(triggerPos + 2, cursorPos - (triggerPos + 2)); + } break; } } + + if (triggered) { + active = &info; + break; + } } if (!active) { @@ -98,9 +115,12 @@ void LineEditCompleter::keyPressEvent(QKeyEvent *event) active->completer->setCompletionPrefix(prefix); - if (active->trigger == "[[") { - emit cardPartialChanged(prefix); - return; + switch (active->trigger) { + case CompleterTrigger::Card: + emit cardPartialChanged(prefix); + return; + case CompleterTrigger::Mention: + break; } active->completer->complete(); @@ -126,51 +146,54 @@ void LineEditCompleter::insertCompletion(QCompleter *completer, const QString &c continue; } - if (info.trigger == "[[") { - int triggerPos = t.lastIndexOf("[[", pos - 1); - if (triggerPos == -1) { + switch (info.trigger) { + case CompleterTrigger::Card: { + int triggerPos = t.lastIndexOf("[[", pos - 1); + if (triggerPos == -1) { + return; + } + + // If an earlier "[[" is still open it also encloses the cursor, so + // replace from its start. Otherwise completing in text such as + // "[[Opt[[Amok" would leave a stray "[[" behind. + int startPos = triggerPos; + for (int searchFrom = triggerPos; searchFrom > 0;) { + const int earlier = t.lastIndexOf("[[", searchFrom - 1); + if (earlier == -1) { + break; + } + const int earlierClose = t.indexOf("]]", earlier + 2); + if (earlierClose != -1 && earlierClose < pos) { + break; + } + startPos = earlier; + searchFrom = earlier; + } + + // If the cursor sits inside an already-closed [[...]] pair, replace + // the whole construct instead of leaving a duplicate closing bracket + // behind. + int insertEnd = pos; + const int closePos = t.indexOf("]]", startPos + 2); + if (closePos != -1 && closePos >= pos) { + insertEnd = closePos + 2; + } + + QString after = t.mid(insertEnd); + QString replaced = t.left(startPos + 2) + completion + "]] "; + setText(replaced + after); + setCursorPosition(replaced.length()); return; } - - // If an earlier "[[" is still open it also encloses the cursor, so - // replace from its start. Otherwise completing in text such as - // "[[Opt[[Amok" would leave a stray "[[" behind. - int startPos = triggerPos; - for (int searchFrom = triggerPos; searchFrom > 0;) { - const int earlier = t.lastIndexOf("[[", searchFrom - 1); - if (earlier == -1) { - break; + case CompleterTrigger::Mention: { + int triggerPos = t.lastIndexOf("@", pos - 1); + if (triggerPos == -1) { + return; } - const int earlierClose = t.indexOf("]]", earlier + 2); - if (earlierClose != -1 && earlierClose < pos) { - break; - } - startPos = earlier; - searchFrom = earlier; + setText(t.replace(triggerPos, pos - triggerPos, completion + " ")); + setCursorPosition(triggerPos + completion.length() + 1); + return; } - - // If the cursor sits inside an already-closed [[...]] pair, replace - // the whole construct instead of leaving a duplicate closing bracket - // behind. - int insertEnd = pos; - const int closePos = t.indexOf("]]", startPos + 2); - if (closePos != -1 && closePos >= pos) { - insertEnd = closePos + 2; - } - - QString after = t.mid(insertEnd); - QString replaced = t.left(startPos + 2) + completion + "]] "; - setText(replaced + after); - setCursorPosition(replaced.length()); - return; } - - int triggerPos = t.lastIndexOf("@", pos - 1); - if (triggerPos == -1) { - return; - } - setText(t.replace(triggerPos, pos - triggerPos, completion + " ")); - setCursorPosition(triggerPos + completion.length() + 1); - return; } } diff --git a/cockatrice/src/interface/widgets/utility/line_edit_completer.h b/cockatrice/src/interface/widgets/utility/line_edit_completer.h index 5338cf6e2..9275bd43d 100644 --- a/cockatrice/src/interface/widgets/utility/line_edit_completer.h +++ b/cockatrice/src/interface/widgets/utility/line_edit_completer.h @@ -17,10 +17,16 @@ #include #include +enum class CompleterTrigger +{ + Mention, // "@" prefix, replaced in place with a trailing space. + Card // "[[" prefix, replaced with the completion plus a closing "]] ". +}; + struct CompleterInfo { QCompleter *completer; - QString trigger; // e.g., "@" or "[[" + CompleterTrigger trigger; }; class LineEditCompleter : public LineEditUnfocusable @@ -43,7 +49,7 @@ protected: public: explicit LineEditCompleter(QWidget *parent = nullptr); - void addCompleter(QCompleter *c, const QString &trigger); + void addCompleter(QCompleter *c, CompleterTrigger trigger); bool hasVisibleCompleterPopup() const; void hideCompleterPopups();