diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index a23e1b60..4cc1445c 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -170,15 +170,13 @@ Rectangle { } else if (event.matches(StandardKey.SelectAll) && popup.opened) { completer.completerName = ""; popup.close(); - } else if (event.matches(StandardKey.InsertLineSeparator)) { - if (popup.opened) - popup.close(); - if (Settings.invertEnterKey) { - room.input.send(); - event.accepted = true; - } - } else if (event.matches(StandardKey.InsertParagraphSeparator)) { - if (popup.opened) { + } else if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { + // Handling popup takes priority over newline and sending message. + if (popup.opened && + (event.modifiers == Qt.NoModifier + || event.modifiers == Qt.ShiftModifier + || event.modifiers == Qt.ControlModifier) + ) { var currentCompletion = completer.currentCompletion(); let userid = completer.currentUserid(); @@ -191,14 +189,26 @@ Rectangle { console.log(userid); room.input.addMention(userid, currentCompletion); } - event.accepted = true; - return; } + event.accepted = true; } - if (!Settings.invertEnterKey) { + // Send message Enter key combination event. + else if (Settings.sendMessageKey == 0 && event.modifiers == Qt.NoModifier + || Settings.sendMessageKey == 1 && event.modifiers == Qt.ShiftModifier + || Settings.sendMessageKey == 2 && event.modifiers == Qt.ControlModifier + ) { room.input.send(); event.accepted = true; } + // Add newline Enter key combination event. + else if (Settings.sendMessageKey == 0 && event.modifiers == Qt.ShiftModifier + || Settings.sendMessageKey == 1 && event.modifiers == Qt.NoModifier + || Settings.sendMessageKey == 2 && event.modifiers == Qt.ShiftModifier + ) { + messageInput.insert(messageInput.cursorPosition, "\n"); + event.accepted = true; + } + // Any other Enter key combo is ignored here. } else if (event.key == Qt.Key_Tab && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.ShiftModifier)) { event.accepted = true; if (popup.opened) { diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 3f348d61..db3bf7f1 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -35,6 +35,14 @@ QSharedPointer UserSettings::instance_; UserSettings::UserSettings() { + if (settings.contains("user/invert_enter_key")) { + auto oldValue = + (settings.value("user/invert_enter_key", false).toBool() ? SendMessageKey::ShiftEnter + : SendMessageKey::Enter); + settings.setValue("user/send_message_key", static_cast(oldValue)); + settings.remove("user/invert_enter_key"); + } + connect( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); }); } @@ -71,8 +79,13 @@ UserSettings::load(std::optional profile) settings.value("user/timeline/message_hover_highlight", false).toBool(); enlargeEmojiOnlyMessages_ = settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool(); - markdown_ = settings.value("user/markdown_enabled", true).toBool(); - invertEnterKey_ = settings.value("user/invert_enter_key", false).toBool(); + markdown_ = settings.value("user/markdown_enabled", true).toBool(); + + auto sendMessageKey = settings.value("user/send_message_key", 0).toInt(); + if (sendMessageKey < 0 || sendMessageKey > 2) + sendMessageKey = static_cast(SendMessageKey::Enter); + sendMessageKey_ = static_cast(sendMessageKey); + bubbles_ = settings.value("user/bubbles_enabled", false).toBool(); smallAvatars_ = settings.value("user/small_avatars_enabled", false).toBool(); animateImagesOnHover_ = settings.value("user/animate_images_on_hover", false).toBool(); @@ -340,13 +353,12 @@ UserSettings::setMarkdown(bool state) } void -UserSettings::setInvertEnterKey(bool state) +UserSettings::setSendMessageKey(SendMessageKey key) { - if (state == invertEnterKey_) + if (key == sendMessageKey_) return; - - invertEnterKey_ = state; - emit invertEnterKeyChanged(state); + sendMessageKey_ = key; + emit sendMessageKeyChanged(key); save(); } @@ -936,7 +948,7 @@ UserSettings::save() settings.setValue("group_view", groupView_); settings.setValue("scrollbars_in_roomlist", scrollbarsInRoomlist_); settings.setValue("markdown_enabled", markdown_); - settings.setValue("invert_enter_key", invertEnterKey_); + settings.setValue("send_message_key", static_cast(sendMessageKey_)); settings.setValue("bubbles_enabled", bubbles_); settings.setValue("small_avatars_enabled", smallAvatars_); settings.setValue("animate_images_on_hover", animateImagesOnHover_); @@ -1050,8 +1062,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const return tr("Scrollbars in room list"); case Markdown: return tr("Send messages as Markdown"); - case InvertEnterKey: - return tr("Use shift+enter to send and enter to start a new line"); + case SendMessageKey: + return tr("Send messages with a shortcut"); case Bubbles: return tr("Enable message bubbles"); case SmallAvatars: @@ -1205,8 +1217,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const return i->scrollbarsInRoomlist(); case Markdown: return i->markdown(); - case InvertEnterKey: - return i->invertEnterKey(); + case SendMessageKey: + return static_cast(i->sendMessageKey()); case Bubbles: return i->bubbles(); case SmallAvatars: @@ -1371,10 +1383,11 @@ UserSettingsModel::data(const QModelIndex &index, int role) const return tr( "Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain " "text."); - case InvertEnterKey: + case SendMessageKey: return tr( - "Invert the behavior of the enter key in the text input, making it send the message " - "when shift+enter is pressed and starting a new line when enter is pressed."); + "Select what Enter key combination sends the message. Shift+Enter adds a new line, " + "unless it has been selected, in which case Enter adds a new line instead.\n\n" + "If an emoji picker or a mention picker is open, it is always handled first."); case Bubbles: return tr( "Messages get a bubble background. This also triggers some layout changes (WIP)."); @@ -1542,6 +1555,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const case CameraFrameRate: case Ringtone: case ShowImage: + case SendMessageKey: return Options; case TimelineMaxWidth: case PrivacyScreenTimeout: @@ -1556,7 +1570,6 @@ UserSettingsModel::data(const QModelIndex &index, int role) const case GroupView: case ScrollbarsInRoomlist: case Markdown: - case InvertEnterKey: case Bubbles: case SmallAvatars: case AnimateImagesOnHover: @@ -1675,6 +1688,12 @@ UserSettingsModel::data(const QModelIndex &index, int role) const tr("Only in private rooms"), tr("Never"), }; + case SendMessageKey: + return QStringList{ + tr("Enter"), + tr("Shift+Enter"), + tr("Ctrl+Enter"), + }; case Microphone: return vecToList(CallDevices::instance().names(false, i->microphone().toStdString())); case Camera: @@ -1816,12 +1835,14 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int } else return false; } - case InvertEnterKey: { - if (value.userType() == QMetaType::Bool) { - i->setInvertEnterKey(value.toBool()); - return true; - } else + case SendMessageKey: { + auto newKey = value.toInt(); + if (newKey < 0 || + QMetaEnum::fromType().keyCount() <= newKey) return false; + + i->setSendMessageKey(static_cast(newKey)); + return true; } case Bubbles: { if (value.userType() == QMetaType::Bool) { @@ -2306,8 +2327,8 @@ UserSettingsModel::UserSettingsModel(QObject *p) connect(s.get(), &UserSettings::markdownChanged, this, [this]() { emit dataChanged(index(Markdown), index(Markdown), {Value}); }); - connect(s.get(), &UserSettings::invertEnterKeyChanged, this, [this]() { - emit dataChanged(index(InvertEnterKey), index(InvertEnterKey), {Value}); + connect(s.get(), &UserSettings::sendMessageKeyChanged, this, [this]() { + emit dataChanged(index(SendMessageKey), index(SendMessageKey), {Value}); }); connect(s.get(), &UserSettings::bubblesChanged, this, [this]() { emit dataChanged(index(Bubbles), index(Bubbles), {Value}); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index 63a4d616..c1c198d2 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -29,8 +29,8 @@ class UserSettings final : public QObject Q_PROPERTY(bool scrollbarsInRoomlist READ scrollbarsInRoomlist WRITE setScrollbarsInRoomlist NOTIFY scrollbarsInRoomlistChanged) Q_PROPERTY(bool markdown READ markdown WRITE setMarkdown NOTIFY markdownChanged) - Q_PROPERTY( - bool invertEnterKey READ invertEnterKey WRITE setInvertEnterKey NOTIFY invertEnterKeyChanged) + Q_PROPERTY(SendMessageKey sendMessageKey READ sendMessageKey WRITE setSendMessageKey NOTIFY + sendMessageKeyChanged) Q_PROPERTY(bool bubbles READ bubbles WRITE setBubbles NOTIFY bubblesChanged) Q_PROPERTY(bool smallAvatars READ smallAvatars WRITE setSmallAvatars NOTIFY smallAvatarsChanged) Q_PROPERTY(bool animateImagesOnHover READ animateImagesOnHover WRITE setAnimateImagesOnHover @@ -166,6 +166,14 @@ public: }; Q_ENUM(ShowImage) + enum class SendMessageKey + { + Enter, + ShiftEnter, + CtrlEnter, + }; + Q_ENUM(SendMessageKey) + void save(); void load(std::optional profile); void applyTheme(); @@ -182,7 +190,7 @@ public: void setGroupView(bool state); void setScrollbarsInRoomlist(bool state); void setMarkdown(bool state); - void setInvertEnterKey(bool state); + void setSendMessageKey(SendMessageKey key); void setBubbles(bool state); void setSmallAvatars(bool state); void setAnimateImagesOnHover(bool state); @@ -255,7 +263,7 @@ public: bool privacyScreen() const { return privacyScreen_; } int privacyScreenTimeout() const { return privacyScreenTimeout_; } bool markdown() const { return markdown_; } - bool invertEnterKey() const { return invertEnterKey_; } + SendMessageKey sendMessageKey() const { return sendMessageKey_; } bool bubbles() const { return bubbles_; } bool smallAvatars() const { return smallAvatars_; } bool animateImagesOnHover() const { return animateImagesOnHover_; } @@ -328,7 +336,7 @@ signals: void trayChanged(bool state); void startInTrayChanged(bool state); void markdownChanged(bool state); - void invertEnterKeyChanged(bool state); + void sendMessageKeyChanged(SendMessageKey key); void bubblesChanged(bool state); void smallAvatarsChanged(bool state); void animateImagesOnHoverChanged(bool state); @@ -399,7 +407,7 @@ private: bool groupView_; bool scrollbarsInRoomlist_; bool markdown_; - bool invertEnterKey_; + SendMessageKey sendMessageKey_; bool bubbles_; bool smallAvatars_; bool animateImagesOnHover_; @@ -510,7 +518,7 @@ class UserSettingsModel : public QAbstractListModel TypingNotifications, ReadReceipts, Markdown, - InvertEnterKey, + SendMessageKey, Bubbles, SmallAvatars,