diff --git a/CMakeLists.txt b/CMakeLists.txt index fb45483e..d64b033a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,7 +316,6 @@ set(SRC_FILES src/MatrixClient.cpp src/MxcImageProvider.cpp src/Olm.cpp - src/QuickSwitcher.cpp src/RegisterPage.cpp src/RoomInfoListItem.cpp src/RoomList.cpp @@ -532,7 +531,6 @@ qt5_wrap_cpp(MOC_HEADERS src/LoginPage.h src/MainWindow.h src/MxcImageProvider.h - src/QuickSwitcher.h src/RegisterPage.h src/RoomInfoListItem.h src/RoomList.h diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml index e1b5c129..6170045c 100644 --- a/resources/qml/Completer.qml +++ b/resources/qml/Completer.qml @@ -15,9 +15,16 @@ Popup { property string completerName property var completer property bool bottomToTop: true + property bool fullWidth: false + property bool centerRowContent: true + property int avatarHeight: 24 + property int avatarWidth: 24 + property int rowMargin: 0 + property int rowSpacing: 5 property alias count: listView.count signal completionClicked(string completion) + signal completionSelected(string id) function up() { if (bottomToTop) @@ -54,9 +61,19 @@ Popup { return null; } + function finishCompletion() { + if(popup.completerName == "room") { + popup.completionSelected(listView.itemAtIndex(currentIndex).modelData.roomid) + } + } + onCompleterNameChanged: { if (completerName) { - completer = TimelineManager.timeline.input.completerFor(completerName); + if (completerName == "user") { + completer = TimelineManager.completerFor(completerName, TimelineManager.timeline.roomId()); + } else { + completer = TimelineManager.completerFor(completerName); + } completer.setSearchString(""); } else { completer = undefined; @@ -75,14 +92,17 @@ Popup { id: listView anchors.fill: parent - implicitWidth: contentItem.childrenRect.width + implicitWidth: fullWidth ? parent.width : contentItem.childrenRect.width model: completer verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom + spacing: rowSpacing + pixelAligned: true delegate: Rectangle { color: model.index == popup.currentIndex ? colors.highlight : colors.base - height: chooser.childrenRect.height + 4 - implicitWidth: chooser.childrenRect.width + 4 + height: chooser.childrenRect.height + 2 * popup.rowMargin + implicitWidth: fullWidth ? popup.width : chooser.childrenRect.width + 4 + property variant modelData: model MouseArea { id: mouseArea @@ -90,7 +110,12 @@ Popup { anchors.fill: parent hoverEnabled: true onPositionChanged: popup.currentIndex = model.index - onClicked: popup.completionClicked(completer.completionAt(model.index)) + onClicked: { + popup.completionClicked(completer.completionAt(model.index)) + if(popup.completerName == "room") { + popup.completionSelected(model.roomid) + } + } Ripple { rippleTarget: mouseArea @@ -103,7 +128,8 @@ Popup { id: chooser roleValue: popup.completerName - anchors.centerIn: parent + anchors.fill: parent + anchors.margins: popup.rowMargin DelegateChoice { roleValue: "user" @@ -112,10 +138,11 @@ Popup { id: del anchors.centerIn: parent + spacing: rowSpacing Avatar { - height: 24 - width: 24 + height: popup.avatarHeight + width: popup.avatarWidth displayName: model.displayName url: model.avatarUrl.replace("mxc://", "image://MxcImage/") onClicked: popup.completionClicked(completer.completionAt(model.index)) @@ -142,6 +169,7 @@ Popup { id: del anchors.centerIn: parent + spacing: rowSpacing Label { text: model.unicode @@ -164,11 +192,41 @@ Popup { RowLayout { id: del - anchors.centerIn: parent + anchors.centerIn: centerRowContent ? parent : undefined + spacing: rowSpacing Avatar { - height: 24 - width: 24 + height: popup.avatarHeight + width: popup.avatarWidth + url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + onClicked: { + popup.completionClicked(completer.completionAt(model.index)) + popup.completionSelected(model.roomid) + } + } + + Label { + text: model.roomName + font.pixelSize: popup.avatarHeight * 0.5 + color: model.index == popup.currentIndex ? colors.highlightedText : colors.text + } + + } + + } + + DelegateChoice { + roleValue: "roomAliases" + + RowLayout { + id: del + + anchors.centerIn: parent + spacing: rowSpacing + + Avatar { + height: popup.avatarHeight + width: popup.avatarWidth url: model.avatarUrl.replace("mxc://", "image://MxcImage/") onClicked: popup.completionClicked(completer.completionAt(model.index)) } diff --git a/resources/qml/MatrixTextField.qml b/resources/qml/MatrixTextField.qml new file mode 100644 index 00000000..9e8d286b --- /dev/null +++ b/resources/qml/MatrixTextField.qml @@ -0,0 +1,56 @@ +import QtQuick 2.13 +import QtQuick.Layouts 1.13 +import QtQuick.Controls 2.13 + +TextField { + id: input + palette: colors + + background: Rectangle { + color: colors.base + } + + Rectangle { + id: blueBar + + anchors.top: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + color: colors.highlight + height: 1 + width: parent.width + + Rectangle { + id: blackBar + + anchors.verticalCenter: blueBar.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + height: parent.height+1 + width: 0 + color: colors.text + + states: State { + name: "focused"; when: input.activeFocus == true + PropertyChanges { + target: blackBar + width: blueBar.width + } + } + + transitions: Transition { + from: "" + to: "focused" + reversible: true + + NumberAnimation { + target: blackBar + properties: "width" + duration: 500 + easing.type: Easing.InOutQuad + alwaysRunToEnd: true + } + } + } + } +} \ No newline at end of file diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index 7b651bc0..faf0d442 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -161,7 +161,7 @@ Rectangle { messageInput.openCompleter(cursorPosition, "emoji"); popup.open(); } else if (event.key == Qt.Key_NumberSign) { - messageInput.openCompleter(cursorPosition, "room"); + messageInput.openCompleter(cursorPosition, "roomAliases"); popup.open(); } else if (event.key == Qt.Key_Escape && popup.opened) { completerTriggeredAt = -1; diff --git a/resources/qml/QuickSwitcher.qml b/resources/qml/QuickSwitcher.qml new file mode 100644 index 00000000..29d9a9b2 --- /dev/null +++ b/resources/qml/QuickSwitcher.qml @@ -0,0 +1,97 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import im.nheko 1.0 + +Popup { + id: quickSwitcher + + property int textHeight: 32 + property int textMargin: 8 + + x: parent.width / 2 - width / 2 + y: parent.height / 4 - height / 2 + width: parent.width / 2 + modal: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + parent: Overlay.overlay + palette: colors + + Overlay.modal: Rectangle { + color: "#aa1E1E1E" + } + + MatrixTextField { + id: roomTextInput + + anchors.fill: parent + font.pixelSize: quickSwitcher.textHeight * 0.6 + padding: textMargin + color: colors.text + + onTextEdited: { + completerPopup.completer.setSearchString(text) + } + + Keys.onPressed: { + if (event.key == Qt.Key_Up && completerPopup.opened) { + event.accepted = true; + completerPopup.up(); + } else if (event.key == Qt.Key_Down && completerPopup.opened) { + event.accepted = true; + completerPopup.down(); + } else if (event.matches(StandardKey.InsertParagraphSeparator)) { + completerPopup.finishCompletion() + event.accepted = true; + } + } + } + + Completer { + id: completerPopup + + x: roomTextInput.x + y: roomTextInput.y + roomTextInput.height + textMargin + width: parent.width + completerName: "room" + bottomToTop: false + fullWidth: true + avatarHeight: textHeight + avatarWidth: textHeight + centerRowContent: false + rowMargin: 8 + rowSpacing: 6 + + closePolicy: Popup.NoAutoClose + } + + onOpened: { + completerPopup.open() + delay(200, function() { + roomTextInput.forceActiveFocus() + }) + } + + onClosed: { + completerPopup.close() + } + + Connections { + onCompletionSelected: { + TimelineManager.setHistoryView(id) + TimelineManager.highlightRoom(id) + quickSwitcher.close() + } + target: completerPopup + } + + Timer { + id: timer + } + + function delay(delayTime, cb) { + timer.interval = delayTime; + timer.repeat = false; + timer.triggered.connect(cb); + timer.start(); + } +} \ No newline at end of file diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 0a823329..20251bc0 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -72,6 +72,22 @@ Page { } + Component { + id: quickSwitcherComponent + + QuickSwitcher { + } + } + + Shortcut { + sequence: "Ctrl+K" + onActivated: { + var quickSwitch = quickSwitcherComponent.createObject(timelineRoot); + TimelineManager.focusTimeline() + quickSwitch.open(); + } + } + Menu { id: messageContextMenu diff --git a/resources/res.qrc b/resources/res.qrc index e629a871..328f65ca 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -129,6 +129,7 @@ qml/EncryptionIndicator.qml qml/ImageButton.qml qml/MatrixText.qml + qml/MatrixTextField.qml qml/ToggleButton.qml qml/MessageInput.qml qml/MessageView.qml @@ -140,6 +141,7 @@ qml/StatusIndicator.qml qml/TimelineRow.qml qml/TopBar.qml + qml/QuickSwitcher.qml qml/TypingIndicator.qml qml/RoomSettings.qml qml/emoji/EmojiButton.qml diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index d7ea4301..7c018aff 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -23,7 +23,6 @@ #include "MainWindow.h" #include "MatrixClient.h" #include "Olm.h" -#include "QuickSwitcher.h" #include "RoomList.h" #include "SideBarActions.h" #include "Splitter.h" @@ -589,18 +588,6 @@ ChatPage::loadStateFromCache() emit trySyncCb(); } -void -ChatPage::showQuickSwitcher() -{ - auto dialog = new QuickSwitcher(this); - - connect(dialog, &QuickSwitcher::roomSelected, room_list_, &RoomList::highlightSelectedRoom); - connect( - dialog, &QuickSwitcher::closing, this, []() { MainWindow::instance()->hideOverlay(); }); - - MainWindow::instance()->showTransparentOverlayModal(dialog); -} - void ChatPage::removeRoom(const QString &room_id) { @@ -1456,3 +1443,9 @@ ChatPage::handleMatrixUri(const QUrl &uri) { handleMatrixUri(uri.toString(QUrl::ComponentFormattingOption::FullyEncoded).toUtf8()); } + +void +ChatPage::highlightRoom(const QString &room_id) +{ + room_list_->highlightSelectedRoom(room_id); +} \ No newline at end of file diff --git a/src/ChatPage.h b/src/ChatPage.h index f2078f45..17a4827f 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -31,7 +31,6 @@ #include "notifications/Manager.h" class OverlayModal; -class QuickSwitcher; class RoomList; class SideBarActions; class Splitter; @@ -72,7 +71,6 @@ public: // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); - void showQuickSwitcher(); QString currentRoom() const { return current_room_; } static ChatPage *instance() { return instance_; } @@ -104,6 +102,7 @@ public slots: void startChat(QString userid); void leaveRoom(const QString &room_id); void createRoom(const mtx::requests::CreateRoom &req); + void highlightRoom(const QString &room_id); void joinRoom(const QString &room); void joinRoomVia(const std::string &room_id, const std::vector &via, diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp index 44a14911..a6759978 100644 --- a/src/CompletionProxyModel.cpp +++ b/src/CompletionProxyModel.cpp @@ -10,8 +10,11 @@ #include "Logging.h" #include "Utils.h" -CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model, QObject *parent) +CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model, + int max_mistakes, + QObject *parent) : QAbstractProxyModel(parent) + , maxMistakes_(max_mistakes) { setSourceModel(model); QRegularExpression splitPoints("\\s+|-"); @@ -63,7 +66,7 @@ CompletionProxyModel::invalidate() { auto key = searchString.toUcs4(); beginResetModel(); - mapping = trie_.search(key, 7); + mapping = trie_.search(key, 7, maxMistakes_); endResetModel(); std::string temp; diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h index 1b8e18f7..214845b7 100644 --- a/src/CompletionProxyModel.h +++ b/src/CompletionProxyModel.h @@ -58,19 +58,19 @@ struct trie } std::vector search(const QVector &keys, //< TODO(Nico): replace this with a span - size_t limit, - size_t max_distance = 2) const + size_t result_count_limit, + size_t max_edit_distance = 2) const { std::vector ret; - if (!limit) + if (!result_count_limit) return ret; if (keys.isEmpty()) - return valuesAndSubvalues(limit); + return valuesAndSubvalues(result_count_limit); - auto append = [&ret, limit](std::vector &&in) { + auto append = [&ret, result_count_limit](std::vector &&in) { for (auto &&v : in) { - if (ret.size() >= limit) + if (ret.size() >= result_count_limit) return; if (std::find(ret.begin(), ret.end(), v) == ret.end()) { @@ -80,11 +80,12 @@ struct trie }; if (auto e = this->next.find(keys[0]); e != this->next.end()) { - append(e->second.search(keys.mid(1), limit, max_distance)); + append( + e->second.search(keys.mid(1), result_count_limit, max_edit_distance)); } - if (max_distance && ret.size() < limit) { - max_distance -= 1; + if (max_edit_distance && ret.size() < result_count_limit) { + max_edit_distance -= 1; // swap chars case if (keys.size() >= 2) { @@ -99,27 +100,31 @@ struct trie } if (t) { - append(t->search( - keys.mid(2), (limit - ret.size()) * 2, max_distance)); + append(t->search(keys.mid(2), + (result_count_limit - ret.size()) * 2, + max_edit_distance)); } } // delete character case - append(this->search(keys.mid(1), (limit - ret.size()) * 2, max_distance)); + append(this->search( + keys.mid(1), (result_count_limit - ret.size()) * 2, max_edit_distance)); // substitute and insert cases for (const auto &[k, t] : this->next) { - if (k == keys[0] || ret.size() >= limit) + if (k == keys[0] || ret.size() >= result_count_limit) break; // substitute - append(t.search(keys.mid(1), limit - ret.size(), max_distance)); + append(t.search( + keys.mid(1), result_count_limit - ret.size(), max_edit_distance)); - if (ret.size() >= limit) + if (ret.size() >= result_count_limit) break; // insert - append(t.search(keys, limit - ret.size(), max_distance)); + append(t.search( + keys, result_count_limit - ret.size(), max_edit_distance)); } } @@ -132,7 +137,9 @@ class CompletionProxyModel : public QAbstractProxyModel Q_OBJECT public: - CompletionProxyModel(QAbstractItemModel *model, QObject *parent = nullptr); + CompletionProxyModel(QAbstractItemModel *model, + int max_mistakes = 2, + QObject *parent = nullptr); void invalidate(); @@ -160,4 +167,5 @@ private: QString searchString; trie trie_; std::vector mapping; + int maxMistakes_; }; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index c405f853..92f43e03 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -135,12 +135,6 @@ MainWindow::MainWindow(QWidget *parent) QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this); connect(quitShortcut, &QShortcut::activated, this, QApplication::quit); - QShortcut *quickSwitchShortcut = new QShortcut(QKeySequence("Ctrl+K"), this); - connect(quickSwitchShortcut, &QShortcut::activated, this, [this]() { - if (chat_page_->isVisible() && !hasActiveDialogs()) - chat_page_->showQuickSwitcher(); - }); - trayIcon_->setVisible(userSettings_->tray()); if (hasActiveUser()) { diff --git a/src/QuickSwitcher.cpp b/src/QuickSwitcher.cpp deleted file mode 100644 index ad2be23d..00000000 --- a/src/QuickSwitcher.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-FileCopyrightText: 2017 Konstantinos Sideris -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include -#include -#include -#include - -#include "Cache.h" -#include "QuickSwitcher.h" -#include "popups/SuggestionsPopup.h" - -Q_DECLARE_METATYPE(std::vector) - -RoomSearchInput::RoomSearchInput(QWidget *parent) - : TextField(parent) -{} - -void -RoomSearchInput::keyPressEvent(QKeyEvent *event) -{ - switch (event->key()) { - case Qt::Key_Tab: - case Qt::Key_Down: { - emit selectNextCompletion(); - event->accept(); - break; - } - case Qt::Key_Backtab: - case Qt::Key_Up: { - emit selectPreviousCompletion(); - event->accept(); - break; - } - default: - TextField::keyPressEvent(event); - } -} - -void -RoomSearchInput::hideEvent(QHideEvent *event) -{ - emit hiding(); - TextField::hideEvent(event); -} - -QuickSwitcher::QuickSwitcher(QWidget *parent) - : QWidget(parent) -{ - qRegisterMetaType>(); - setMaximumWidth(450); - - QFont font; - font.setPointSizeF(font.pointSizeF() * 1.5); - - roomSearch_ = new RoomSearchInput(this); - roomSearch_->setFont(font); - roomSearch_->setPlaceholderText(tr("Search for a room...")); - - topLayout_ = new QVBoxLayout(this); - topLayout_->addWidget(roomSearch_); - - connect(this, - &QuickSwitcher::queryResults, - this, - [this](const std::vector &rooms) { - auto pos = mapToGlobal(roomSearch_->geometry().bottomLeft()); - - popup_.setFixedWidth(width()); - popup_.addRooms(rooms); - popup_.move(pos.x() - topLayout_->margin(), pos.y() + topLayout_->margin()); - popup_.show(); - }); - - connect(roomSearch_, &QLineEdit::textEdited, this, [this](const QString &query) { - if (query.isEmpty()) { - popup_.hide(); - return; - } - - QtConcurrent::run([this, query = query.toLower()]() { - try { - emit queryResults(cache::searchRooms(query.toStdString())); - } catch (const lmdb::error &e) { - qWarning() << "room search failed:" << e.what(); - } - }); - }); - - connect(roomSearch_, - &RoomSearchInput::selectNextCompletion, - &popup_, - &SuggestionsPopup::selectNextSuggestion); - connect(roomSearch_, - &RoomSearchInput::selectPreviousCompletion, - &popup_, - &SuggestionsPopup::selectPreviousSuggestion); - connect(&popup_, &SuggestionsPopup::itemSelected, this, [this](const QString &room_id) { - reset(); - emit roomSelected(room_id); - }); - connect(roomSearch_, &RoomSearchInput::hiding, this, [this]() { popup_.hide(); }); - connect(roomSearch_, &QLineEdit::returnPressed, this, [this]() { - reset(); - popup_.selectHoveredSuggestion(); - }); -} - -void -QuickSwitcher::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void -QuickSwitcher::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) { - event->accept(); - reset(); - } -} diff --git a/src/QuickSwitcher.h b/src/QuickSwitcher.h deleted file mode 100644 index 41c53016..00000000 --- a/src/QuickSwitcher.h +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: 2017 Konstantinos Sideris -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include -#include - -#include "popups/SuggestionsPopup.h" -#include "ui/TextField.h" - -class RoomSearchInput : public TextField -{ - Q_OBJECT -public: - explicit RoomSearchInput(QWidget *parent = nullptr); - -signals: - void selectNextCompletion(); - void selectPreviousCompletion(); - void hiding(); - -protected: - void keyPressEvent(QKeyEvent *event) override; - void hideEvent(QHideEvent *event) override; - bool focusNextPrevChild(bool) override { return false; }; -}; - -class QuickSwitcher : public QWidget -{ - Q_OBJECT - -public: - QuickSwitcher(QWidget *parent = nullptr); - -signals: - void closing(); - void roomSelected(const QString &roomid); - void queryResults(const std::vector &rooms); - -protected: - void keyPressEvent(QKeyEvent *event) override; - void showEvent(QShowEvent *) override { roomSearch_->setFocus(); } - void paintEvent(QPaintEvent *event) override; - -private: - void reset() - { - emit closing(); - roomSearch_->clear(); - } - - // Current highlighted selection from the completer. - int selection_ = -1; - - QVBoxLayout *topLayout_; - RoomSearchInput *roomSearch_; - - //! Autocomplete popup box with the room suggestions. - SuggestionsPopup popup_; -}; diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index 490fccc3..d5a6a1dd 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -192,28 +192,6 @@ InputBar::nextText() return text(); } -QObject * -InputBar::completerFor(QString completerName) -{ - if (completerName == "user") { - auto userModel = new UsersModel(room->roomId().toStdString()); - auto proxy = new CompletionProxyModel(userModel); - userModel->setParent(proxy); - return proxy; - } else if (completerName == "emoji") { - auto emojiModel = new emoji::EmojiModel(); - auto proxy = new CompletionProxyModel(emojiModel); - emojiModel->setParent(proxy); - return proxy; - } else if (completerName == "room") { - auto roomModel = new RoomsModel(true); - auto proxy = new CompletionProxyModel(roomModel); - roomModel->setParent(proxy); - return proxy; - } - return nullptr; -} - void InputBar::send() { diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index 5af46b0d..acd9e22c 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -55,8 +55,6 @@ public slots: bool uploading() const { return uploading_; } void message(QString body, MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED); - QObject *completerFor(QString completerName); - private slots: void startTyping(); void stopTyping(); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 74427855..3ed1c21c 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -15,13 +15,16 @@ #include "BlurhashProvider.h" #include "ChatPage.h" #include "ColorImageProvider.h" +#include "CompletionProxyModel.h" #include "DelegateChooser.h" #include "DeviceVerificationFlow.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" #include "MxcImageProvider.h" +#include "RoomsModel.h" #include "UserSettingsPage.h" +#include "UsersModel.h" #include "dialogs/ImageOverlay.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" @@ -334,6 +337,12 @@ TimelineViewManager::setHistoryView(const QString &room_id) } } +void +TimelineViewManager::highlightRoom(const QString &room_id) +{ + ChatPage::instance()->highlightRoom(room_id); +} + QString TimelineViewManager::escapeEmoji(QString str) const { @@ -556,3 +565,36 @@ TimelineViewManager::focusMessageInput() { emit focusInput(); } + +QObject * +TimelineViewManager::completerFor(QString completerName, QString roomId) +{ + if (completerName == "user") { + auto userModel = new UsersModel(roomId.toStdString()); + auto proxy = new CompletionProxyModel(userModel); + userModel->setParent(proxy); + return proxy; + } else if (completerName == "emoji") { + auto emojiModel = new emoji::EmojiModel(); + auto proxy = new CompletionProxyModel(emojiModel); + emojiModel->setParent(proxy); + return proxy; + } else if (completerName == "room") { + auto roomModel = new RoomsModel(false); + auto proxy = new CompletionProxyModel(roomModel, 4); + roomModel->setParent(proxy); + return proxy; + } else if (completerName == "roomAliases") { + auto roomModel = new RoomsModel(true); + auto proxy = new CompletionProxyModel(roomModel); + roomModel->setParent(proxy); + return proxy; + } + return nullptr; +} + +void +TimelineViewManager::focusTimeline() +{ + getWidget()->setFocus(); +} \ No newline at end of file diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 097fccfc..e3ed4991 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -104,6 +104,8 @@ public slots: } void setHistoryView(const QString &room_id); + void highlightRoom(const QString &room_id); + void focusTimeline(); TimelineModel *getHistoryView(const QString &room_id) { auto room = models.find(room_id); @@ -142,6 +144,7 @@ public slots: } void backToRooms() { emit showRoomList(); } + QObject *completerFor(QString completerName, QString roomId = ""); private: #ifdef USE_QUICK_VIEW