summary refs log tree commit diff
path: root/src/popups
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2020-02-28 19:10:39 -0500
committerGitHub <noreply@github.com>2020-02-28 19:10:39 -0500
commit30cb7c5b02bb8a21a8d7257cfe5a8bd3db891606 (patch)
treeddadd16c86600ce937b47798a9b3d5ff2fdec998 /src/popups
parentMerge pull request #129 from nico202/master (diff)
parentMerge branch 'master' into 0.7.0-dev (diff)
downloadnheko-30cb7c5b02bb8a21a8d7257cfe5a8bd3db891606.tar.xz
Merge pull request #130 from Nheko-Reborn/0.7.0-dev
0.7.0 dev merge to master
Diffstat (limited to 'src/popups')
-rw-r--r--src/popups/PopupItem.cpp141
-rw-r--r--src/popups/PopupItem.h83
-rw-r--r--src/popups/ReplyPopup.cpp103
-rw-r--r--src/popups/ReplyPopup.h44
-rw-r--r--src/popups/SuggestionsPopup.cpp156
-rw-r--r--src/popups/SuggestionsPopup.h75
-rw-r--r--src/popups/UserMentions.cpp171
-rw-r--r--src/popups/UserMentions.h45
8 files changed, 818 insertions, 0 deletions
diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp
new file mode 100644

index 00000000..db97e4a3 --- /dev/null +++ b/src/popups/PopupItem.cpp
@@ -0,0 +1,141 @@ +#include <QPaintEvent> +#include <QPainter> +#include <QStyleOption> + +#include "../Utils.h" +#include "../ui/Avatar.h" +#include "PopupItem.h" + +constexpr int PopupHMargin = 4; +constexpr int PopupItemMargin = 3; + +PopupItem::PopupItem(QWidget *parent) + : QWidget(parent) + , avatar_{new Avatar(this, conf::popup::avatar)} + , hovering_{false} +{ + setMouseTracking(true); + setAttribute(Qt::WA_Hover); + + topLayout_ = new QHBoxLayout(this); + topLayout_->setContentsMargins( + PopupHMargin, PopupItemMargin, PopupHMargin, PopupItemMargin); + + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +void +PopupItem::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + if (underMouse() || hovering_) + p.fillRect(rect(), hoverColor_); +} + +UserItem::UserItem(QWidget *parent) + : PopupItem(parent) +{ + userName_ = new QLabel("Placeholder", this); + avatar_->setLetter("P"); + topLayout_->addWidget(avatar_); + topLayout_->addWidget(userName_, 1); +} + +UserItem::UserItem(QWidget *parent, const QString &user_id) + : PopupItem(parent) + , userId_{user_id} +{ + auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_); + + avatar_->setLetter(utils::firstChar(displayName)); + + // If it's a matrix id we use the second letter. + if (displayName.size() > 1 && displayName.at(0) == '@') + avatar_->setLetter(QChar(displayName.at(1))); + + userName_ = new QLabel(displayName, this); + + topLayout_->addWidget(avatar_); + topLayout_->addWidget(userName_, 1); + + resolveAvatar(user_id); +} + +void +UserItem::updateItem(const QString &user_id) +{ + userId_ = user_id; + + auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_); + + // If it's a matrix id we use the second letter. + if (displayName.size() > 1 && displayName.at(0) == '@') + avatar_->setLetter(QChar(displayName.at(1))); + else + avatar_->setLetter(utils::firstChar(displayName)); + + userName_->setText(displayName); + resolveAvatar(user_id); +} + +void +UserItem::resolveAvatar(const QString &user_id) +{ + avatar_->setImage(ChatPage::instance()->currentRoom(), user_id); +} + +void +UserItem::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() != Qt::RightButton) + emit clicked( + cache::displayName(ChatPage::instance()->currentRoom(), selectedText())); + + QWidget::mousePressEvent(event); +} + +RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res) + : PopupItem(parent) + , roomId_{QString::fromStdString(res.room_id)} +{ + auto name = QFontMetrics(QFont()).elidedText( + QString::fromStdString(res.info.name), Qt::ElideRight, parentWidget()->width() - 10); + + avatar_->setLetter(utils::firstChar(name)); + + roomName_ = new QLabel(name, this); + roomName_->setMargin(0); + + topLayout_->addWidget(avatar_); + topLayout_->addWidget(roomName_, 1); + + avatar_->setImage(QString::fromStdString(res.info.avatar_url)); +} + +void +RoomItem::updateItem(const RoomSearchResult &result) +{ + roomId_ = QString::fromStdString(std::move(result.room_id)); + + auto name = + QFontMetrics(QFont()).elidedText(QString::fromStdString(std::move(result.info.name)), + Qt::ElideRight, + parentWidget()->width() - 10); + + roomName_->setText(name); + + avatar_->setImage(QString::fromStdString(result.info.avatar_url)); +} + +void +RoomItem::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() != Qt::RightButton) + emit clicked(selectedText()); + + QWidget::mousePressEvent(event); +} diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h new file mode 100644
index 00000000..7a710fdb --- /dev/null +++ b/src/popups/PopupItem.h
@@ -0,0 +1,83 @@ +#pragma once + +#include <QHBoxLayout> +#include <QLabel> +#include <QPoint> +#include <QWidget> + +#include "../AvatarProvider.h" +#include "../ChatPage.h" + +class Avatar; +struct SearchResult; + +class PopupItem : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor) + Q_PROPERTY(bool hovering READ hovering WRITE setHovering) + +public: + PopupItem(QWidget *parent); + + QString selectedText() const { return QString(); } + QColor hoverColor() const { return hoverColor_; } + void setHoverColor(QColor &color) { hoverColor_ = color; } + + bool hovering() const { return hovering_; } + void setHovering(const bool hover) { hovering_ = hover; }; + +protected: + void paintEvent(QPaintEvent *event) override; + +signals: + void clicked(const QString &text); + +protected: + QHBoxLayout *topLayout_; + Avatar *avatar_; + QColor hoverColor_; + + //! Set if the item is currently being + //! hovered during tab completion (cycling). + bool hovering_; +}; + +class UserItem : public PopupItem +{ + Q_OBJECT + +public: + UserItem(QWidget *parent); + UserItem(QWidget *parent, const QString &user_id); + QString selectedText() const { return userId_; } + void updateItem(const QString &user_id); + +protected: + void mousePressEvent(QMouseEvent *event) override; + +private: + void resolveAvatar(const QString &user_id); + + QLabel *userName_; + QString userId_; +}; + +class RoomItem : public PopupItem +{ + Q_OBJECT + +public: + RoomItem(QWidget *parent, const RoomSearchResult &res); + QString selectedText() const { return roomId_; } + void updateItem(const RoomSearchResult &res); + +protected: + void mousePressEvent(QMouseEvent *event) override; + +private: + QLabel *roomName_; + QString roomId_; + RoomSearchResult info_; +}; diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp new file mode 100644
index 00000000..5058c039 --- /dev/null +++ b/src/popups/ReplyPopup.cpp
@@ -0,0 +1,103 @@ +#include <QLabel> +#include <QPaintEvent> +#include <QPainter> +#include <QStyleOption> + +#include "../Config.h" +#include "../Utils.h" +#include "../ui/Avatar.h" +#include "../ui/DropShadow.h" +#include "../ui/TextLabel.h" +#include "PopupItem.h" +#include "ReplyPopup.h" + +ReplyPopup::ReplyPopup(QWidget *parent) + : QWidget(parent) + , userItem_{nullptr} + , msgLabel_{nullptr} + , eventLabel_{nullptr} +{ + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); + + mainLayout_ = new QVBoxLayout(this); + mainLayout_->setMargin(0); + mainLayout_->setSpacing(0); + + topLayout_ = new QHBoxLayout(); + topLayout_->setSpacing(0); + topLayout_->setContentsMargins(13, 1, 13, 0); + + userItem_ = new UserItem(this); + connect(userItem_, &UserItem::clicked, this, &ReplyPopup::userSelected); + topLayout_->addWidget(userItem_); + + buttonLayout_ = new QHBoxLayout(); + buttonLayout_->setSpacing(0); + buttonLayout_->setMargin(0); + + topLayout_->addLayout(buttonLayout_); + QFont f; + f.setPointSizeF(f.pointSizeF()); + const int fontHeight = QFontMetrics(f).height(); + buttonSize_ = std::min(fontHeight, 20); + + closeBtn_ = new FlatButton(this); + closeBtn_->setToolTip(tr("Logout")); + closeBtn_->setCornerRadius(buttonSize_ / 4); + closeBtn_->setText("X"); + + QIcon icon; + icon.addFile(":/icons/icons/ui/remove-symbol.png"); + + closeBtn_->setIcon(icon); + closeBtn_->setIconSize(QSize(buttonSize_, buttonSize_)); + connect(closeBtn_, &FlatButton::clicked, this, [this]() { emit cancel(); }); + + buttonLayout_->addWidget(closeBtn_); + + topLayout_->addLayout(buttonLayout_); + + mainLayout_->addLayout(topLayout_); + msgLabel_ = new TextLabel(this); + msgLabel_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); + mainLayout_->addWidget(msgLabel_); + eventLabel_ = new QLabel(this); + mainLayout_->addWidget(eventLabel_); + + setLayout(mainLayout_); +} + +void +ReplyPopup::setReplyContent(const RelatedInfo &related) +{ + // Update the current widget with the new data. + userItem_->updateItem(related.quoted_user); + + msgLabel_->setText(utils::getFormattedQuoteBody(related, "") + .replace("<mx-reply>", "") + .replace("</mx-reply>", "")); + + // eventLabel_->setText(srcEvent); + + adjustSize(); +} + +void +ReplyPopup::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void +ReplyPopup::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() != Qt::RightButton) { + emit clicked(eventLabel_->text()); + } + + QWidget::mousePressEvent(event); +} diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h new file mode 100644
index 00000000..1fa3bb83 --- /dev/null +++ b/src/popups/ReplyPopup.h
@@ -0,0 +1,44 @@ +#pragma once + +#include <QHBoxLayout> +#include <QLabel> +#include <QVBoxLayout> +#include <QWidget> + +#include "../ui/FlatButton.h" +#include "../ui/TextLabel.h" + +struct RelatedInfo; +class UserItem; + +class ReplyPopup : public QWidget +{ + Q_OBJECT + +public: + explicit ReplyPopup(QWidget *parent = nullptr); + +public slots: + void setReplyContent(const RelatedInfo &related); + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +signals: + void userSelected(const QString &user); + void clicked(const QString &text); + void cancel(); + +private: + QHBoxLayout *topLayout_; + QVBoxLayout *mainLayout_; + QHBoxLayout *buttonLayout_; + + UserItem *userItem_; + FlatButton *closeBtn_; + TextLabel *msgLabel_; + QLabel *eventLabel_; + + int buttonSize_; +}; diff --git a/src/popups/SuggestionsPopup.cpp b/src/popups/SuggestionsPopup.cpp new file mode 100644
index 00000000..8f355b38 --- /dev/null +++ b/src/popups/SuggestionsPopup.cpp
@@ -0,0 +1,156 @@ +#include <QPaintEvent> +#include <QPainter> +#include <QStyleOption> + +#include "../Config.h" +#include "../Utils.h" +#include "../ui/Avatar.h" +#include "../ui/DropShadow.h" +#include "SuggestionsPopup.h" + +SuggestionsPopup::SuggestionsPopup(QWidget *parent) + : QWidget(parent) +{ + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint); + + layout_ = new QVBoxLayout(this); + layout_->setMargin(0); + layout_->setSpacing(0); +} + +void +SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms) +{ + if (rooms.empty()) { + hide(); + return; + } + + const size_t layoutCount = layout_->count(); + const size_t roomCount = rooms.size(); + + // Remove the extra widgets from the layout. + if (roomCount < layoutCount) + removeLayoutItemsAfter(roomCount - 1); + + for (size_t i = 0; i < roomCount; ++i) { + auto item = layout_->itemAt(i); + + // Create a new widget if there isn't already one in that + // layout position. + if (!item) { + auto room = new RoomItem(this, rooms.at(i)); + connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected); + layout_->addWidget(room); + } else { + // Update the current widget with the new data. + auto room = qobject_cast<RoomItem *>(item->widget()); + if (room) + room->updateItem(rooms.at(i)); + } + } + + resetSelection(); + adjustSize(); + + resize(geometry().width(), 40 * rooms.size()); + + selectNextSuggestion(); +} + +void +SuggestionsPopup::addUsers(const std::vector<SearchResult> &users) +{ + if (users.empty()) { + hide(); + return; + } + + const size_t layoutCount = layout_->count(); + const size_t userCount = users.size(); + + // Remove the extra widgets from the layout. + if (userCount < layoutCount) + removeLayoutItemsAfter(userCount - 1); + + for (size_t i = 0; i < userCount; ++i) { + auto item = layout_->itemAt(i); + + // Create a new widget if there isn't already one in that + // layout position. + if (!item) { + auto user = new UserItem(this, users.at(i).user_id); + connect(user, &UserItem::clicked, this, &SuggestionsPopup::itemSelected); + layout_->addWidget(user); + } else { + // Update the current widget with the new data. + auto userWidget = qobject_cast<UserItem *>(item->widget()); + if (userWidget) + userWidget->updateItem(users.at(i).user_id); + } + } + + resetSelection(); + adjustSize(); + + selectNextSuggestion(); +} + +void +SuggestionsPopup::hoverSelection() +{ + resetHovering(); + setHovering(selectedItem_); + update(); +} + +void +SuggestionsPopup::selectNextSuggestion() +{ + selectedItem_++; + if (selectedItem_ >= layout_->count()) + selectFirstItem(); + + hoverSelection(); +} + +void +SuggestionsPopup::selectPreviousSuggestion() +{ + selectedItem_--; + if (selectedItem_ < 0) + selectLastItem(); + + hoverSelection(); +} + +void +SuggestionsPopup::resetHovering() +{ + for (int i = 0; i < layout_->count(); ++i) { + const auto item = qobject_cast<PopupItem *>(layout_->itemAt(i)->widget()); + + if (item) + item->setHovering(false); + } +} + +void +SuggestionsPopup::setHovering(int pos) +{ + const auto &item = layout_->itemAt(pos); + const auto &widget = qobject_cast<PopupItem *>(item->widget()); + + if (widget) + widget->setHovering(true); +} + +void +SuggestionsPopup::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h new file mode 100644
index 00000000..63c44538 --- /dev/null +++ b/src/popups/SuggestionsPopup.h
@@ -0,0 +1,75 @@ +#pragma once + +#include <QHBoxLayout> +#include <QLabel> +#include <QPoint> +#include <QWidget> + +#include "CacheStructs.h" +#include "ChatPage.h" +#include "PopupItem.h" + +class SuggestionsPopup : public QWidget +{ + Q_OBJECT + +public: + explicit SuggestionsPopup(QWidget *parent = nullptr); + + template<class Item> + void selectHoveredSuggestion() + { + const auto item = layout_->itemAt(selectedItem_); + if (!item) + return; + + const auto &widget = qobject_cast<Item *>(item->widget()); + emit itemSelected( + cache::displayName(ChatPage::instance()->currentRoom(), widget->selectedText())); + + resetSelection(); + } + +public slots: + void addUsers(const std::vector<SearchResult> &users); + void addRooms(const std::vector<RoomSearchResult> &rooms); + + //! Move to the next available suggestion item. + void selectNextSuggestion(); + //! Move to the previous available suggestion item. + void selectPreviousSuggestion(); + //! Remove hovering from all items. + void resetHovering(); + //! Set hovering to the item in the given layout position. + void setHovering(int pos); + +protected: + void paintEvent(QPaintEvent *event) override; + +signals: + void itemSelected(const QString &user); + +private: + void hoverSelection(); + void resetSelection() { selectedItem_ = -1; } + void selectFirstItem() { selectedItem_ = 0; } + void selectLastItem() { selectedItem_ = layout_->count() - 1; } + void removeLayoutItemsAfter(size_t startingPos) + { + size_t posToRemove = layout_->count() - 1; + + QLayoutItem *item; + while (startingPos <= posToRemove && + (item = layout_->takeAt(posToRemove)) != nullptr) { + delete item->widget(); + delete item; + + posToRemove = layout_->count() - 1; + } + } + + QVBoxLayout *layout_; + + //! Counter for tab completion (cycling). + int selectedItem_ = -1; +}; diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp new file mode 100644
index 00000000..2e70dbd3 --- /dev/null +++ b/src/popups/UserMentions.cpp
@@ -0,0 +1,171 @@ +#include <QPaintEvent> +#include <QPainter> +#include <QScrollArea> +#include <QStyleOption> +#include <QTabWidget> +#include <QTimer> +#include <QVBoxLayout> + +#include "Cache.h" +#include "ChatPage.h" +#include "Logging.h" +#include "UserMentions.h" +//#include "timeline/TimelineItem.h" + +using namespace popups; + +UserMentions::UserMentions(QWidget *parent) + : QWidget{parent} +{ + setAttribute(Qt::WA_ShowWithoutActivating, true); + setWindowFlags(Qt::FramelessWindowHint | Qt::Popup); + + tab_layout_ = new QTabWidget(this); + + top_layout_ = new QVBoxLayout(this); + top_layout_->setSpacing(0); + top_layout_->setMargin(0); + + local_scroll_area_ = new QScrollArea(this); + local_scroll_area_->setWidgetResizable(true); + local_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + local_scroll_widget_ = new QWidget(this); + local_scroll_widget_->setObjectName("local_scroll_widget"); + + all_scroll_area_ = new QScrollArea(this); + all_scroll_area_->setWidgetResizable(true); + all_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + all_scroll_widget_ = new QWidget(this); + all_scroll_widget_->setObjectName("all_scroll_widget"); + + // Height of the typing display. + QFont f; + f.setPointSizeF(f.pointSizeF() * 0.9); + const int bottomMargin = QFontMetrics(f).height() + 6; + + local_scroll_layout_ = new QVBoxLayout(local_scroll_widget_); + local_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); + local_scroll_layout_->setSpacing(0); + local_scroll_layout_->setObjectName("localscrollarea"); + + all_scroll_layout_ = new QVBoxLayout(all_scroll_widget_); + all_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin); + all_scroll_layout_->setSpacing(0); + all_scroll_layout_->setObjectName("allscrollarea"); + + local_scroll_area_->setWidget(local_scroll_widget_); + local_scroll_area_->setAlignment(Qt::AlignBottom); + + all_scroll_area_->setWidget(all_scroll_widget_); + all_scroll_area_->setAlignment(Qt::AlignBottom); + + tab_layout_->addTab(local_scroll_area_, tr("This Room")); + tab_layout_->addTab(all_scroll_area_, tr("All Rooms")); + top_layout_->addWidget(tab_layout_); + + setLayout(top_layout_); +} + +void +UserMentions::initializeMentions(const QMap<QString, mtx::responses::Notifications> &notifs) +{ + nhlog::ui()->debug("Initializing " + std::to_string(notifs.size()) + " notifications."); + + for (const auto &item : notifs) { + for (const auto &notif : item.notifications) { + const auto event_id = QString::fromStdString(utils::event_id(notif.event)); + + try { + const auto room_id = QString::fromStdString(notif.room_id); + const auto user_id = utils::event_sender(notif.event); + const auto body = utils::event_body(notif.event); + + pushItem(event_id, + user_id, + body, + room_id, + ChatPage::instance()->currentRoom()); + + } catch (const lmdb::error &e) { + nhlog::db()->warn("error while sending desktop notification: {}", + e.what()); + } + } + } +} + +void +UserMentions::showPopup() +{ + for (auto widget : all_scroll_layout_->findChildren<QWidget *>()) { + delete widget; + } + for (auto widget : local_scroll_layout_->findChildren<QWidget *>()) { + delete widget; + } + + auto notifs = cache::getTimelineMentions(); + + initializeMentions(notifs); + show(); +} + +void +UserMentions::pushItem(const QString &event_id, + const QString &user_id, + const QString &body, + const QString &room_id, + const QString &current_room_id) +{ + (void)event_id; + (void)user_id; + (void)body; + (void)room_id; + (void)current_room_id; + // setUpdatesEnabled(false); + // + // // Add to the 'all' section + // TimelineItem *view_item = new TimelineItem( + // mtx::events::MessageType::Text, user_id, body, true, room_id, + // all_scroll_widget_); + // view_item->setEventId(event_id); + // view_item->hide(); + // + // all_scroll_layout_->addWidget(view_item); + // QTimer::singleShot(0, this, [view_item, this]() { + // view_item->show(); + // view_item->adjustSize(); + // setUpdatesEnabled(true); + // }); + // + // // if it matches the current room... add it to the current room as well. + // if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) { + // // Add to the 'local' section + // TimelineItem *local_view_item = new + // TimelineItem(mtx::events::MessageType::Text, + // user_id, + // body, + // true, + // room_id, + // local_scroll_widget_); + // local_view_item->setEventId(event_id); + // local_view_item->hide(); + // local_scroll_layout_->addWidget(local_view_item); + // + // QTimer::singleShot(0, this, [local_view_item]() { + // local_view_item->show(); + // local_view_item->adjustSize(); + // }); + // } +} + +void +UserMentions::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h new file mode 100644
index 00000000..b7c4e51d --- /dev/null +++ b/src/popups/UserMentions.h
@@ -0,0 +1,45 @@ +#pragma once + +#include <mtx/responses.hpp> + +#include <QMap> +#include <QString> +#include <QWidget> + +class QPaintEvent; +class QTabWidget; +class QScrollArea; +class QVBoxLayout; + +namespace popups { + +class UserMentions : public QWidget +{ + Q_OBJECT +public: + UserMentions(QWidget *parent = nullptr); + + void initializeMentions(const QMap<QString, mtx::responses::Notifications> &notifs); + void showPopup(); + +protected: + void paintEvent(QPaintEvent *) override; + +private: + void pushItem(const QString &event_id, + const QString &user_id, + const QString &body, + const QString &room_id, + const QString &current_room_id); + QTabWidget *tab_layout_; + QVBoxLayout *top_layout_; + QVBoxLayout *local_scroll_layout_; + QVBoxLayout *all_scroll_layout_; + + QScrollArea *local_scroll_area_; + QWidget *local_scroll_widget_; + + QScrollArea *all_scroll_area_; + QWidget *all_scroll_widget_; +}; +}