Port top bar to Qml

Also fixes some resize issues with stupid workarounds to our resize
logic. This really needs to be cleaned up at some point!
This commit is contained in:
Nicolas Werner 2020-09-03 17:01:58 +02:00
parent c4e4938d35
commit 640b0ee405
12 changed files with 267 additions and 448 deletions

View file

@ -304,7 +304,6 @@ set(SRC_FILES
src/SideBarActions.cpp src/SideBarActions.cpp
src/Splitter.cpp src/Splitter.cpp
src/TextInputWidget.cpp src/TextInputWidget.cpp
src/TopRoomBar.cpp
src/TrayIcon.cpp src/TrayIcon.cpp
src/UserInfoWidget.cpp src/UserInfoWidget.cpp
src/UserSettingsPage.cpp src/UserSettingsPage.cpp
@ -512,7 +511,6 @@ qt5_wrap_cpp(MOC_HEADERS
src/SideBarActions.h src/SideBarActions.h
src/Splitter.h src/Splitter.h
src/TextInputWidget.h src/TextInputWidget.h
src/TopRoomBar.h
src/TrayIcon.h src/TrayIcon.h
src/UserInfoWidget.h src/UserInfoWidget.h
src/UserSettingsPage.h src/UserSettingsPage.h

View file

@ -50,6 +50,8 @@ Rectangle {
anchors.bottom: avatar.bottom anchors.bottom: avatar.bottom
anchors.right: avatar.right anchors.right: avatar.right
visible: !!userid
height: avatar.height / 6 height: avatar.height / 6
width: height width: height
radius: settings.avatarCircles ? height / 2 : height / 4 radius: settings.avatarCircles ? height / 2 : height / 4

View file

@ -115,6 +115,112 @@ Page {
z: 3 z: 3
} }
ColumnLayout {
anchors.fill: parent
Rectangle {
id: topBar
Layout.fillWidth: true
implicitHeight: topLayout.height + 16
z: 3
color: colors.base
GridLayout {
id: topLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 8
anchors.verticalCenter: parent.verticalCenter
//Layout.margins: 8
ImageButton {
id: backToRoomsButton
Layout.column: 0
Layout.row: 0
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
visible: timelineManager.isNarrowView
image: ":/icons/icons/ui/angle-pointing-to-left.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Back to room list")
onClicked: timelineManager.backToRooms()
}
Avatar {
Layout.column: 1
Layout.row: 0
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
width: avatarSize
height: avatarSize
url: chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
displayName: chat.model.roomName
}
Label {
Layout.fillWidth: true
Layout.column: 2
Layout.row: 0
font.pointSize: fontMetrics.font.pointSize * 1.1
text: chat.model.roomName
}
MatrixText {
Layout.fillWidth: true
Layout.column: 2
Layout.row: 1
text: chat.model.roomTopic
Layout.maximumHeight: fontMetrics.lineSpacing * 2 // show 2 lines
clip: true
}
ImageButton {
id: roomOptionsButton
Layout.column: 3
Layout.row: 0
Layout.rowSpan: 2
Layout.alignment: Qt.AlignVCenter
image: ":/icons/icons/ui/vertical-ellipsis.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Room options")
onClicked: roomOptionsMenu.popup(roomOptionsButton)
Menu {
id: roomOptionsMenu
MenuItem {
text: qsTr("Invite users")
onTriggered: timelineManager.openInviteUsersDialog();
}
MenuItem {
text: qsTr("Members")
onTriggered: timelineManager.openMemberListDialog();
}
MenuItem {
text: qsTr("Leave room")
onTriggered: timelineManager.openLeaveRoomDialog();
}
MenuItem {
text: qsTr("Settings")
onTriggered: timelineManager.openRoomSettings();
}
}
}
}
}
ListView { ListView {
id: chat id: chat
@ -122,13 +228,8 @@ Page {
cacheBuffer: 400 cacheBuffer: 400
anchors.horizontalCenter: parent.horizontalCenter Layout.fillWidth: true
anchors.top: parent.top Layout.fillHeight: true
anchors.bottom: chatFooter.top
width: parent.width
anchors.leftMargin: 4
anchors.rightMargin: scrollbar.width
model: timelineManager.timeline model: timelineManager.timeline
@ -167,10 +268,6 @@ Page {
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
id: scrollbar id: scrollbar
parent: chat.parent
anchors.top: chat.top
anchors.right: chat.right
anchors.bottom: chat.bottom
} }
spacing: 4 spacing: 4
@ -178,9 +275,9 @@ Page {
onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom onCountChanged: if (atYEnd) model.currentIndex = 0 // Mark last event as read, since we are at the bottom
property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32) property int delegateMaxWidth: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > scrollbar.width*2) ? settings.timelineMaxWidth : (parent.width - scrollbar.width*2)
delegate: Rectangle { delegate: Item {
// This would normally be previousSection, but our model's order is inverted. // This would normally be previousSection, but our model's order is inverted.
property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1 property bool sectionBoundary: (ListView.nextSection != "" && ListView.nextSection !== ListView.section) || model.index === chat.count - 1
@ -189,7 +286,6 @@ Page {
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
width: chat.delegateMaxWidth width: chat.delegateMaxWidth
height: section ? section.height + timelinerow.height : timelinerow.height height: section ? section.height + timelinerow.height : timelinerow.height
color: "transparent"
TimelineRow { TimelineRow {
id: timelinerow id: timelinerow
@ -309,17 +405,13 @@ Page {
} }
} }
Rectangle { Item {
id: chatFooter id: chatFooter
height: Math.max(fontMetrics.height * 1.2, footerContent.height) implicitHeight: Math.max(fontMetrics.height * 1.2, footerContent.height)
anchors.left: parent.left Layout.fillWidth: true
anchors.right: parent.right
anchors.bottom: parent.bottom
z: 3 z: 3
color: "transparent"
Column { Column {
id: footerContent id: footerContent
anchors.left: parent.left anchors.left: parent.left
@ -382,4 +474,5 @@ Page {
} }
} }
} }
}
} }

View file

@ -37,7 +37,6 @@
#include "SideBarActions.h" #include "SideBarActions.h"
#include "Splitter.h" #include "Splitter.h"
#include "TextInputWidget.h" #include "TextInputWidget.h"
#include "TopRoomBar.h"
#include "UserInfoWidget.h" #include "UserInfoWidget.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
#include "Utils.h" #include "Utils.h"
@ -126,10 +125,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
contentLayout_->setSpacing(0); contentLayout_->setSpacing(0);
contentLayout_->setMargin(0); contentLayout_->setMargin(0);
top_bar_ = new TopRoomBar(this);
view_manager_ = new TimelineViewManager(userSettings_, &callManager_, this); view_manager_ = new TimelineViewManager(userSettings_, &callManager_, this);
contentLayout_->addWidget(top_bar_);
contentLayout_->addWidget(view_manager_->getWidget()); contentLayout_->addWidget(view_manager_->getWidget());
activeCallBar_ = new ActiveCallBar(this); activeCallBar_ = new ActiveCallBar(this);
@ -181,30 +178,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
room_list_->previousRoom(); room_list_->previousRoom();
}); });
connect(top_bar_, &TopRoomBar::mentionsClicked, this, [this](const QPoint &mentionsPos) {
if (user_mentions_popup_->isVisible()) {
user_mentions_popup_->hide();
} else {
showNotificationsDialog(mentionsPos);
http::client()->notifications(
1000,
"",
"highlight",
[this, mentionsPos](const mtx::responses::Notifications &res,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn(
"failed to retrieve notifications: {} ({})",
err->matrix_error.error,
static_cast<int>(err->status_code));
return;
}
emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
});
}
});
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL); connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() { connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
if (http::client()->access_token().empty()) { if (http::client()->access_token().empty()) {
@ -226,8 +199,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect(this, &ChatPage::loggedOut, this, &ChatPage::logout); connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
connect(top_bar_, &TopRoomBar::showRoomList, splitter, &Splitter::showFullRoomList); connect(
connect(top_bar_, &TopRoomBar::inviteUsers, this, [this](QStringList users) { view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList);
connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) {
const auto room_id = current_room_.toStdString(); const auto room_id = current_room_.toStdString();
for (int ii = 0; ii < users.size(); ++ii) { for (int ii = 0; ii < users.size(); ++ii) {
@ -252,7 +226,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
}); });
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping); connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView); connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView);
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
connect( connect(
@ -487,8 +460,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
} }
}); });
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
connect( connect(
this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities); this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities);
@ -588,11 +559,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
}); });
connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync); connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags); connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags);
connect(
this, &ChatPage::syncTopBar, this, [this](const std::map<QString, RoomInfo> &updates) {
if (updates.find(currentRoom()) != updates.end())
changeTopRoomInfo(currentRoom());
});
// Callbacks to update the user info (top left corner of the page). // Callbacks to update the user info (top left corner of the page).
connect(this, &ChatPage::setUserAvatar, user_info_widget_, &UserInfoWidget::setAvatar); connect(this, &ChatPage::setUserAvatar, user_info_widget_, &UserInfoWidget::setAvatar);
@ -657,7 +623,6 @@ void
ChatPage::resetUI() ChatPage::resetUI()
{ {
room_list_->clear(); room_list_->clear();
top_bar_->reset();
user_info_widget_->reset(); user_info_widget_->reset();
view_manager_->clearAll(); view_manager_->clearAll();
@ -786,46 +751,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
tryInitialSync(); tryInitialSync();
} }
void
ChatPage::updateTopBarAvatar(const QString &roomid, const QString &img)
{
if (current_room_ != roomid)
return;
top_bar_->updateRoomAvatar(img);
}
void
ChatPage::changeTopRoomInfo(const QString &room_id)
{
if (room_id.isEmpty()) {
nhlog::ui()->warn("cannot switch to empty room_id");
return;
}
try {
auto room_info = cache::getRoomInfo({room_id.toStdString()});
if (room_info.find(room_id) == room_info.end())
return;
const auto name = QString::fromStdString(room_info[room_id].name);
const auto avatar_url = QString::fromStdString(room_info[room_id].avatar_url);
top_bar_->updateRoomName(name);
top_bar_->updateRoomTopic(QString::fromStdString(room_info[room_id].topic));
top_bar_->updateRoomAvatarFromName(name);
if (!avatar_url.isEmpty())
top_bar_->updateRoomAvatar(avatar_url);
} catch (const lmdb::error &e) {
nhlog::ui()->error("failed to change top bar room info: {}", e.what());
}
current_room_ = room_id;
}
void void
ChatPage::showUnreadMessageNotification(int count) ChatPage::showUnreadMessageNotification(int count)
{ {
@ -1070,7 +995,6 @@ ChatPage::handleSyncResponse(mtx::responses::Sync res)
auto updates = cache::roomUpdates(res); auto updates = cache::roomUpdates(res);
emit syncTopBar(updates);
emit syncRoomlist(updates); emit syncRoomlist(updates);
emit syncUI(res.rooms); emit syncUI(res.rooms);
@ -1481,9 +1405,12 @@ ChatPage::getProfileInfo()
void void
ChatPage::hideSideBars() ChatPage::hideSideBars()
{ {
communitiesList_->hide(); // Don't hide side bar, if we are currently only showing the side bar!
sideBar_->hide(); if (view_manager_->getWidget()->isVisible()) {
top_bar_->enableBackButton(); communitiesList_->hide();
sideBar_->hide();
}
view_manager_->enableBackButton();
} }
void void
@ -1493,23 +1420,19 @@ ChatPage::showSideBars()
communitiesList_->show(); communitiesList_->show();
sideBar_->show(); sideBar_->show();
top_bar_->disableBackButton(); view_manager_->disableBackButton();
content_->show();
} }
uint64_t uint64_t
ChatPage::timelineWidth() ChatPage::timelineWidth()
{ {
int sidebarWidth = sideBar_->size().width(); int sidebarWidth = sideBar_->minimumSize().width();
sidebarWidth += communitiesList_->size().width(); sidebarWidth += communitiesList_->minimumSize().width();
nhlog::ui()->info("timelineWidth: {}", size().width() - sidebarWidth);
return size().width() - sidebarWidth; return size().width() - sidebarWidth;
} }
bool
ChatPage::isSideBarExpanded()
{
const auto sz = splitter::calculateSidebarSizes(QFont{});
return sideBar_->size().width() > sz.normal;
}
void void
ChatPage::initiateLogout() ChatPage::initiateLogout()

View file

@ -49,7 +49,6 @@ class SideBarActions;
class Splitter; class Splitter;
class TextInputWidget; class TextInputWidget;
class TimelineViewManager; class TimelineViewManager;
class TopRoomBar;
class UserInfoWidget; class UserInfoWidget;
class UserSettings; class UserSettings;
@ -82,7 +81,6 @@ public:
//! Calculate the width of the message timeline. //! Calculate the width of the message timeline.
uint64_t timelineWidth(); uint64_t timelineWidth();
bool isSideBarExpanded();
//! Hide the room & group list (if it was visible). //! Hide the room & group list (if it was visible).
void hideSideBars(); void hideSideBars();
//! Show the room/group list (if it was visible). //! Show the room/group list (if it was visible).
@ -150,7 +148,6 @@ signals:
void syncUI(const mtx::responses::Rooms &rooms); void syncUI(const mtx::responses::Rooms &rooms);
void syncRoomlist(const std::map<QString, RoomInfo> &updates); void syncRoomlist(const std::map<QString, RoomInfo> &updates);
void syncTags(const std::map<QString, RoomInfo> &updates); void syncTags(const std::map<QString, RoomInfo> &updates);
void syncTopBar(const std::map<QString, RoomInfo> &updates);
void dropToLoginPageCb(const QString &msg); void dropToLoginPageCb(const QString &msg);
void notifyMessage(const QString &roomid, void notifyMessage(const QString &roomid,
@ -167,8 +164,6 @@ signals:
private slots: private slots:
void showUnreadMessageNotification(int count); void showUnreadMessageNotification(int count);
void updateTopBarAvatar(const QString &roomid, const QString &img);
void changeTopRoomInfo(const QString &room_id);
void logout(); void logout();
void removeRoom(const QString &room_id); void removeRoom(const QString &room_id);
void dropToLoginPage(const QString &msg); void dropToLoginPage(const QString &msg);
@ -239,7 +234,6 @@ private:
TimelineViewManager *view_manager_; TimelineViewManager *view_manager_;
SideBarActions *sidebarActions_; SideBarActions *sidebarActions_;
TopRoomBar *top_bar_;
TextInputWidget *text_input_; TextInputWidget *text_input_;
ActiveCallBar *activeCallBar_; ActiveCallBar *activeCallBar_;

View file

@ -200,7 +200,8 @@ MainWindow::adjustSideBars()
const uint64_t timelineWidth = chat_page_->timelineWidth(); const uint64_t timelineWidth = chat_page_->timelineWidth();
const uint64_t minAvailableWidth = sz.collapsePoint + sz.groups; const uint64_t minAvailableWidth = sz.collapsePoint + sz.groups;
if (timelineWidth < minAvailableWidth && !chat_page_->isSideBarExpanded()) { nhlog::ui()->info("timelineWidth: {}, min {}", timelineWidth, minAvailableWidth);
if (timelineWidth < minAvailableWidth) {
chat_page_->hideSideBars(); chat_page_->hideSideBars();
} else { } else {
chat_page_->showSideBars(); chat_page_->showSideBars();

View file

@ -1,229 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QAction>
#include <QIcon>
#include <QLabel>
#include <QPaintEvent>
#include <QPainter>
#include <QPen>
#include <QPoint>
#include <QStyle>
#include <QStyleOption>
#include <QVBoxLayout>
#include "Config.h"
#include "MainWindow.h"
#include "TopRoomBar.h"
#include "Utils.h"
#include "ui/Avatar.h"
#include "ui/FlatButton.h"
#include "ui/Menu.h"
#include "ui/OverlayModal.h"
#include "ui/TextLabel.h"
TopRoomBar::TopRoomBar(QWidget *parent)
: QWidget(parent)
, buttonSize_{32}
{
QFont f;
f.setPointSizeF(f.pointSizeF());
const int fontHeight = QFontMetrics(f).height();
const int widgetMargin = fontHeight / 3;
const int contentHeight = fontHeight * 3;
setFixedHeight(contentHeight + widgetMargin);
topLayout_ = new QHBoxLayout(this);
topLayout_->setSpacing(widgetMargin);
topLayout_->setContentsMargins(
2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin);
avatar_ = new Avatar(this, fontHeight * 2);
avatar_->setLetter("");
textLayout_ = new QVBoxLayout();
textLayout_->setSpacing(0);
textLayout_->setMargin(0);
QFont roomFont;
roomFont.setPointSizeF(roomFont.pointSizeF() * 1.1);
roomFont.setWeight(QFont::Medium);
nameLabel_ = new QLabel(this);
nameLabel_->setFont(roomFont);
nameLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
QFont descriptionFont;
topicLabel_ = new TextLabel(this);
topicLabel_->setLineWrapMode(QTextEdit::NoWrap);
topicLabel_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
topicLabel_->setFont(descriptionFont);
topicLabel_->setTextInteractionFlags(Qt::TextBrowserInteraction);
topicLabel_->setOpenExternalLinks(true);
topicLabel_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
textLayout_->addWidget(nameLabel_);
textLayout_->addWidget(topicLabel_);
settingsBtn_ = new FlatButton(this);
settingsBtn_->setToolTip(tr("Room options"));
settingsBtn_->setFixedSize(buttonSize_, buttonSize_);
settingsBtn_->setCornerRadius(buttonSize_ / 2);
mentionsBtn_ = new FlatButton(this);
mentionsBtn_->setToolTip(tr("Mentions"));
mentionsBtn_->setFixedSize(buttonSize_, buttonSize_);
mentionsBtn_->setCornerRadius(buttonSize_ / 2);
QIcon settings_icon;
settings_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png");
settingsBtn_->setIcon(settings_icon);
settingsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
QIcon mentions_icon;
mentions_icon.addFile(":/icons/icons/ui/at-solid.svg");
mentionsBtn_->setIcon(mentions_icon);
mentionsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
backBtn_ = new FlatButton(this);
backBtn_->setFixedSize(buttonSize_, buttonSize_);
backBtn_->setCornerRadius(buttonSize_ / 2);
QIcon backIcon;
backIcon.addFile(":/icons/icons/ui/angle-pointing-to-left.png");
backBtn_->setIcon(backIcon);
backBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
backBtn_->hide();
connect(backBtn_, &QPushButton::clicked, this, &TopRoomBar::showRoomList);
topLayout_->addWidget(avatar_);
topLayout_->addWidget(backBtn_);
topLayout_->addLayout(textLayout_, 1);
topLayout_->addWidget(mentionsBtn_, 0, Qt::AlignRight);
topLayout_->addWidget(settingsBtn_, 0, Qt::AlignRight);
menu_ = new Menu(this);
inviteUsers_ = new QAction(tr("Invite users"), this);
connect(inviteUsers_, &QAction::triggered, this, [this]() {
MainWindow::instance()->openInviteUsersDialog(
[this](const QStringList &invitees) { emit inviteUsers(invitees); });
});
roomMembers_ = new QAction(tr("Members"), this);
connect(roomMembers_, &QAction::triggered, this, []() {
MainWindow::instance()->openMemberListDialog();
});
leaveRoom_ = new QAction(tr("Leave room"), this);
connect(leaveRoom_, &QAction::triggered, this, []() {
MainWindow::instance()->openLeaveRoomDialog();
});
roomSettings_ = new QAction(tr("Settings"), this);
connect(roomSettings_, &QAction::triggered, this, []() {
MainWindow::instance()->openRoomSettings();
});
menu_->addAction(inviteUsers_);
menu_->addAction(roomMembers_);
menu_->addAction(leaveRoom_);
menu_->addAction(roomSettings_);
connect(settingsBtn_, &QPushButton::clicked, this, [this]() {
auto pos = mapToGlobal(settingsBtn_->pos());
menu_->popup(
QPoint(pos.x() + buttonSize_ - menu_->sizeHint().width(), pos.y() + buttonSize_));
});
connect(mentionsBtn_, &QPushButton::clicked, this, [this]() {
auto pos = mapToGlobal(mentionsBtn_->pos());
emit mentionsClicked(pos);
});
}
void
TopRoomBar::enableBackButton()
{
avatar_->hide();
backBtn_->show();
}
void
TopRoomBar::disableBackButton()
{
avatar_->show();
backBtn_->hide();
}
void
TopRoomBar::updateRoomAvatarFromName(const QString &name)
{
avatar_->setLetter(utils::firstChar(name));
update();
}
void
TopRoomBar::reset()
{
nameLabel_->setText("");
topicLabel_->setText("");
avatar_->setLetter("");
}
void
TopRoomBar::updateRoomAvatar(const QString &avatar_image)
{
avatar_->setImage(avatar_image);
update();
}
void
TopRoomBar::updateRoomName(const QString &name)
{
nameLabel_->setText(name);
update();
}
void
TopRoomBar::updateRoomTopic(QString topic)
{
topic.replace(conf::strings::url_regex, conf::strings::url_html);
topicLabel_->clearLinks();
topicLabel_->setHtml(topic);
update();
}
void
TopRoomBar::mousePressEvent(QMouseEvent *)
{
if (roomSettings_ != nullptr)
roomSettings_->trigger();
}
void
TopRoomBar::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View file

@ -1,90 +0,0 @@
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QColor>
#include <QStringList>
#include <QWidget>
class Avatar;
class FlatButton;
class Menu;
class TextLabel;
class OverlayModal;
class QLabel;
class QHBoxLayout;
class QVBoxLayout;
class TopRoomBar : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
public:
TopRoomBar(QWidget *parent = nullptr);
void updateRoomAvatar(const QString &avatar_image);
void updateRoomName(const QString &name);
void updateRoomTopic(QString topic);
void updateRoomAvatarFromName(const QString &name);
void reset();
QColor borderColor() const { return borderColor_; }
void setBorderColor(QColor &color) { borderColor_ = color; }
public slots:
//! Add a "back-arrow" button that can switch to roomlist only view.
void enableBackButton();
//! Replace the "back-arrow" button with the avatar of the room.
void disableBackButton();
signals:
void inviteUsers(QStringList users);
void showRoomList();
void mentionsClicked(const QPoint &pos);
protected:
void mousePressEvent(QMouseEvent *) override;
void paintEvent(QPaintEvent *) override;
private:
QHBoxLayout *topLayout_ = nullptr;
QVBoxLayout *textLayout_ = nullptr;
QLabel *nameLabel_ = nullptr;
TextLabel *topicLabel_ = nullptr;
Menu *menu_;
QAction *leaveRoom_ = nullptr;
QAction *roomMembers_ = nullptr;
QAction *roomSettings_ = nullptr;
QAction *inviteUsers_ = nullptr;
FlatButton *settingsBtn_;
FlatButton *mentionsBtn_;
FlatButton *backBtn_;
Avatar *avatar_;
int buttonSize_;
QColor borderColor_;
};

View file

@ -517,6 +517,25 @@ TimelineModel::fetchMore(const QModelIndex &)
events.fetchMore(); events.fetchMore();
} }
void
TimelineModel::syncState(const mtx::responses::State &s)
{
using namespace mtx::events;
for (const auto &e : s.events) {
if (std::holds_alternative<StateEvent<state::Avatar>>(e))
emit roomAvatarUrlChanged();
else if (std::holds_alternative<StateEvent<state::Name>>(e))
emit roomNameChanged();
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
emit roomTopicChanged();
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
emit roomAvatarUrlChanged();
emit roomNameChanged();
}
}
}
void void
TimelineModel::addEvents(const mtx::responses::Timeline &timeline) TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
{ {
@ -526,6 +545,7 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
events.handleSync(timeline); events.handleSync(timeline);
using namespace mtx::events; using namespace mtx::events;
for (auto e : timeline.events) { for (auto e : timeline.events) {
if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) { if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) {
MegolmSessionIndex index; MegolmSessionIndex index;
@ -549,6 +569,16 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
emit newCallEvent(event); emit newCallEvent(event);
}, },
e); e);
else if (std::holds_alternative<StateEvent<state::Avatar>>(e))
emit roomAvatarUrlChanged();
else if (std::holds_alternative<StateEvent<state::Name>>(e))
emit roomNameChanged();
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
emit roomTopicChanged();
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
emit roomAvatarUrlChanged();
emit roomNameChanged();
}
} }
updateLastMessage(); updateLastMessage();
} }
@ -1594,3 +1624,37 @@ TimelineModel::formatMemberEvent(QString id)
return rendered; return rendered;
} }
QString
TimelineModel::roomName() const
{
auto info = cache::getRoomInfo({room_id_.toStdString()});
if (!info.count(room_id_))
return "";
else
return QString::fromStdString(info[room_id_].name);
}
QString
TimelineModel::roomAvatarUrl() const
{
auto info = cache::getRoomInfo({room_id_.toStdString()});
if (!info.count(room_id_))
return "";
else
return QString::fromStdString(info[room_id_].avatar_url);
}
QString
TimelineModel::roomTopic() const
{
auto info = cache::getRoomInfo({room_id_.toStdString()});
if (!info.count(room_id_))
return "";
else
return utils::replaceEmoji(utils::linkifyMessage(
utils::escapeBlacklistedHtml(QString::fromStdString(info[room_id_].topic))));
}

View file

@ -137,6 +137,9 @@ class TimelineModel : public QAbstractListModel
Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply) Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
Q_PROPERTY( Q_PROPERTY(
bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged) bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged)
Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged)
public: public:
explicit TimelineModel(TimelineViewManager *manager, explicit TimelineModel(TimelineViewManager *manager,
@ -217,6 +220,7 @@ public:
void updateLastMessage(); void updateLastMessage();
void addEvents(const mtx::responses::Timeline &events); void addEvents(const mtx::responses::Timeline &events);
void syncState(const mtx::responses::State &state);
template<class T> template<class T>
void sendMessageEvent(const T &content, mtx::events::EventType eventType); void sendMessageEvent(const T &content, mtx::events::EventType eventType);
RelatedInfo relatedInfo(QString id); RelatedInfo relatedInfo(QString id);
@ -253,6 +257,10 @@ public slots:
void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; } void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
void clearTimeline() { events.clearTimeline(); } void clearTimeline() { events.clearTimeline(); }
QString roomName() const;
QString roomTopic() const;
QString roomAvatarUrl() const;
private slots: private slots:
void addPendingMessage(mtx::events::collections::TimelineEvents event); void addPendingMessage(mtx::events::collections::TimelineEvents event);
@ -270,6 +278,10 @@ signals:
void newMessageToSend(mtx::events::collections::TimelineEvents event); void newMessageToSend(mtx::events::collections::TimelineEvents event);
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event); void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
void roomNameChanged();
void roomTopicChanged();
void roomAvatarUrlChanged();
private: private:
void sendEncryptedMessageEvent(const std::string &txn_id, void sendEncryptedMessageEvent(const std::string &txn_id,
nlohmann::json content, nlohmann::json content,

View file

@ -12,6 +12,7 @@
#include "ColorImageProvider.h" #include "ColorImageProvider.h"
#include "DelegateChooser.h" #include "DelegateChooser.h"
#include "Logging.h" #include "Logging.h"
#include "MainWindow.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "MxcImageProvider.h" #include "MxcImageProvider.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
@ -76,7 +77,7 @@ TimelineViewManager::userStatus(QString id) const
TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings, TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings,
CallManager *callManager, CallManager *callManager,
QWidget *parent) ChatPage *parent)
: imgProvider(new MxcImageProvider()) : imgProvider(new MxcImageProvider())
, colorImgProvider(new ColorImageProvider()) , colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider()) , blurhashProvider(new BlurhashProvider())
@ -131,15 +132,12 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin
view->engine()->addImageProvider("blurhash", blurhashProvider); view->engine()->addImageProvider("blurhash", blurhashProvider);
view->setSource(QUrl("qrc:///qml/TimelineView.qml")); view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
connect(dynamic_cast<ChatPage *>(parent), connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
&ChatPage::themeChanged, connect(parent,
this,
&TimelineViewManager::updateColorPalette);
connect(dynamic_cast<ChatPage *>(parent),
&ChatPage::decryptSidebarChanged, &ChatPage::decryptSidebarChanged,
this, this,
&TimelineViewManager::updateEncryptedDescriptions); &TimelineViewManager::updateEncryptedDescriptions);
connect(dynamic_cast<ChatPage *>(parent), &ChatPage::loggedOut, this, [this]() { connect(parent, &ChatPage::loggedOut, this, [this]() {
isInitialSync_ = true; isInitialSync_ = true;
emit initialSyncChanged(true); emit initialSyncChanged(true);
}); });
@ -157,6 +155,7 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
&TimelineModel::newCallEvent, &TimelineModel::newCallEvent,
callManager_, callManager_,
&CallManager::syncEvent); &CallManager::syncEvent);
room_model->syncState(room.state);
room_model->addEvents(room.timeline); room_model->addEvents(room.timeline);
if (!isInitialSync_) if (!isInitialSync_)
disconnect(room_model.data(), disconnect(room_model.data(),
@ -245,6 +244,28 @@ TimelineViewManager::openLink(QString link) const
QDesktopServices::openUrl(link); QDesktopServices::openUrl(link);
} }
void
TimelineViewManager::openInviteUsersDialog()
{
MainWindow::instance()->openInviteUsersDialog(
[this](const QStringList &invitees) { emit inviteUsers(invitees); });
}
void
TimelineViewManager::openMemberListDialog() const
{
MainWindow::instance()->openMemberListDialog();
}
void
TimelineViewManager::openLeaveRoomDialog() const
{
MainWindow::instance()->openLeaveRoomDialog();
}
void
TimelineViewManager::openRoomSettings() const
{
MainWindow::instance()->openRoomSettings();
}
void void
TimelineViewManager::updateReadReceipts(const QString &room_id, TimelineViewManager::updateReadReceipts(const QString &room_id,
const std::vector<QString> &event_ids) const std::vector<QString> &event_ids)

View file

@ -21,6 +21,7 @@ class BlurhashProvider;
class CallManager; class CallManager;
class ColorImageProvider; class ColorImageProvider;
class UserSettings; class UserSettings;
class ChatPage;
class TimelineViewManager : public QObject class TimelineViewManager : public QObject
{ {
@ -30,11 +31,13 @@ class TimelineViewManager : public QObject
TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged) TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged)
Q_PROPERTY( Q_PROPERTY(
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
Q_PROPERTY(
bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged)
public: public:
TimelineViewManager(QSharedPointer<UserSettings> userSettings, TimelineViewManager(QSharedPointer<UserSettings> userSettings,
CallManager *callManager, CallManager *callManager,
QWidget *parent = nullptr); ChatPage *parent = nullptr);
QWidget *getWidget() const { return container; } QWidget *getWidget() const { return container; }
void sync(const mtx::responses::Rooms &rooms); void sync(const mtx::responses::Rooms &rooms);
@ -44,6 +47,7 @@ public:
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
bool isNarrowView() const { return isNarrowView_; }
Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const;
Q_INVOKABLE QColor userColor(QString id, QColor background); Q_INVOKABLE QColor userColor(QString id, QColor background);
@ -52,6 +56,11 @@ public:
Q_INVOKABLE void openLink(QString link) const; Q_INVOKABLE void openLink(QString link) const;
Q_INVOKABLE void openInviteUsersDialog();
Q_INVOKABLE void openMemberListDialog() const;
Q_INVOKABLE void openLeaveRoomDialog() const;
Q_INVOKABLE void openRoomSettings() const;
signals: signals:
void clearRoomMessageCount(QString roomid); void clearRoomMessageCount(QString roomid);
void updateRoomsLastMessage(QString roomid, const DescInfo &info); void updateRoomsLastMessage(QString roomid, const DescInfo &info);
@ -59,6 +68,9 @@ signals:
void initialSyncChanged(bool isInitialSync); void initialSyncChanged(bool isInitialSync);
void replyingEventChanged(QString replyingEvent); void replyingEventChanged(QString replyingEvent);
void replyClosed(); void replyClosed();
void inviteUsers(QStringList users);
void showRoomList();
void narrowViewChanged();
public slots: public slots:
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
@ -108,6 +120,23 @@ public slots:
timeline_->clearTimeline(); timeline_->clearTimeline();
} }
void enableBackButton()
{
if (isNarrowView_)
return;
isNarrowView_ = true;
emit narrowViewChanged();
}
void disableBackButton()
{
if (!isNarrowView_)
return;
isNarrowView_ = false;
emit narrowViewChanged();
}
void backToRooms() { emit showRoomList(); }
private: private:
#ifdef USE_QUICK_VIEW #ifdef USE_QUICK_VIEW
QQuickView *view; QQuickView *view;
@ -125,6 +154,7 @@ private:
CallManager *callManager_ = nullptr; CallManager *callManager_ = nullptr;
bool isInitialSync_ = true; bool isInitialSync_ = true;
bool isNarrowView_ = false;
QSharedPointer<UserSettings> settings; QSharedPointer<UserSettings> settings;
QHash<QString, QColor> userColors; QHash<QString, QColor> userColors;