Port remaining sidebar actions to qml

This commit is contained in:
Nicolas Werner 2021-05-30 12:41:44 +02:00
parent 567078d39f
commit 53fcf7f428
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
10 changed files with 50 additions and 677 deletions

View file

@ -477,6 +477,7 @@ Page {
image: ":/icons/icons/ui/power-button-off.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Logout")
onClicked: Nheko.openLogoutDialog()
}
}
@ -523,6 +524,23 @@ Page {
ToolTip.visible: hovered
ToolTip.text: qsTr("Start a new chat")
Layout.margins: Nheko.paddingMedium
onClicked: roomJoinCreateMenu.open(parent)
Platform.Menu {
id: roomJoinCreateMenu
Platform.MenuItem {
text: qsTr("Join a room")
onTriggered: Nheko.openJoinRoomDialog()
}
Platform.MenuItem {
text: qsTr("Create a new room")
onTriggered: Nheko.openCreateRoomDialog()
}
}
}
ImageButton {
@ -545,6 +563,7 @@ Page {
ToolTip.visible: hovered
ToolTip.text: qsTr("User settings")
Layout.margins: Nheko.paddingMedium
onClicked: Nheko.showUserSettingsPage()
}
}

View file

@ -22,7 +22,6 @@
#include "MainWindow.h"
#include "MatrixClient.h"
#include "RegisterPage.h"
#include "Splitter.h"
#include "TrayIcon.h"
#include "UserSettingsPage.h"
#include "Utils.h"

View file

@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QIcon>
#include <QPainter>
#include <QResizeEvent>
#include <QStyle>
#include <QStyleOption>
#include <mtx/requests.hpp>
#include "Config.h"
#include "MainWindow.h"
#include "SideBarActions.h"
#include "Splitter.h"
#include "ui/FlatButton.h"
#include "ui/Menu.h"
SideBarActions::SideBarActions(QWidget *parent)
: QWidget{parent}
{
QFont f;
f.setPointSizeF(f.pointSizeF());
const int fontHeight = QFontMetrics(f).height();
const int contentHeight = fontHeight * 2.5;
setFixedHeight(contentHeight);
layout_ = new QHBoxLayout(this);
layout_->setMargin(0);
QIcon settingsIcon;
settingsIcon.addFile(":/icons/icons/ui/settings.png");
QIcon createRoomIcon;
createRoomIcon.addFile(":/icons/icons/ui/add-square-button.png");
QIcon joinRoomIcon;
joinRoomIcon.addFile(":/icons/icons/ui/speech-bubbles-comment-option.png");
settingsBtn_ = new FlatButton(this);
settingsBtn_->setToolTip(tr("User settings"));
settingsBtn_->setIcon(settingsIcon);
settingsBtn_->setCornerRadius(conf::sidebarActions::iconSize / 2);
settingsBtn_->setIconSize(
QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize));
addMenu_ = new Menu(this);
createRoomAction_ = new QAction(tr("Create new room"), this);
joinRoomAction_ = new QAction(tr("Join a room"), this);
connect(joinRoomAction_, &QAction::triggered, this, [this]() {
MainWindow::instance()->openJoinRoomDialog(
[this](const QString &room_id) { emit joinRoom(room_id); });
});
connect(createRoomAction_, &QAction::triggered, this, [this]() {
MainWindow::instance()->openCreateRoomDialog(
[this](const mtx::requests::CreateRoom &req) { emit createRoom(req); });
});
addMenu_->addAction(createRoomAction_);
addMenu_->addAction(joinRoomAction_);
createRoomBtn_ = new FlatButton(this);
createRoomBtn_->setToolTip(tr("Start a new chat"));
createRoomBtn_->setIcon(createRoomIcon);
createRoomBtn_->setCornerRadius(conf::sidebarActions::iconSize / 2);
createRoomBtn_->setIconSize(
QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize));
connect(createRoomBtn_, &QPushButton::clicked, this, [this]() {
auto pos = mapToGlobal(createRoomBtn_->pos());
auto padding = conf::sidebarActions::iconSize / 2;
addMenu_->popup(
QPoint(pos.x() + padding, pos.y() - padding - addMenu_->sizeHint().height()));
});
roomDirectory_ = new FlatButton(this);
roomDirectory_->setToolTip(tr("Room directory"));
roomDirectory_->setEnabled(false);
roomDirectory_->setIcon(joinRoomIcon);
roomDirectory_->setCornerRadius(conf::sidebarActions::iconSize / 2);
roomDirectory_->setIconSize(
QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize));
layout_->addWidget(createRoomBtn_);
layout_->addWidget(roomDirectory_);
layout_->addWidget(settingsBtn_);
connect(settingsBtn_, &QPushButton::clicked, this, &SideBarActions::showSettings);
}
void
SideBarActions::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
const auto sidebarSizes = splitter::calculateSidebarSizes(QFont{});
if (width() <= sidebarSizes.small) {
roomDirectory_->hide();
createRoomBtn_->hide();
} else {
roomDirectory_->show();
createRoomBtn_->show();
}
}
void
SideBarActions::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View file

@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QAction>
#include <QHBoxLayout>
#include <QWidget>
namespace mtx {
namespace requests {
struct CreateRoom;
}
}
class Menu;
class FlatButton;
class QResizeEvent;
class SideBarActions : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
public:
SideBarActions(QWidget *parent = nullptr);
QColor borderColor() const { return borderColor_; }
void setBorderColor(QColor &color) { borderColor_ = color; }
signals:
void showSettings();
void joinRoom(const QString &room);
void createRoom(const mtx::requests::CreateRoom &request);
protected:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
private:
QHBoxLayout *layout_;
Menu *addMenu_;
QAction *createRoomAction_;
QAction *joinRoomAction_;
FlatButton *settingsBtn_;
FlatButton *createRoomBtn_;
FlatButton *roomDirectory_;
QColor borderColor_;
};

View file

@ -1,166 +0,0 @@
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QSettings>
#include "Logging.h"
#include "Splitter.h"
constexpr auto MaxWidth = (1 << 24) - 1;
Splitter::Splitter(QWidget *parent)
: QSplitter(parent)
, sz_{splitter::calculateSidebarSizes(QFont{})}
{
connect(this, &QSplitter::splitterMoved, this, &Splitter::onSplitterMoved);
setChildrenCollapsible(false);
}
void
Splitter::restoreSizes(int fallback)
{
QSettings settings;
int savedWidth = settings.value("sidebar/width").toInt();
auto left = widget(0);
if (savedWidth <= 0) {
hideSidebar();
return;
} else if (savedWidth <= sz_.small) {
if (left) {
left->setMinimumWidth(sz_.small);
left->setMaximumWidth(sz_.small);
return;
}
} else if (savedWidth < sz_.normal) {
savedWidth = sz_.normal;
}
left->setMinimumWidth(sz_.normal);
left->setMaximumWidth(2 * sz_.normal);
setSizes({savedWidth, fallback - savedWidth});
setStretchFactor(0, 0);
setStretchFactor(1, 1);
}
Splitter::~Splitter()
{
auto left = widget(0);
if (left) {
QSettings settings;
settings.setValue("sidebar/width", left->width());
}
}
void
Splitter::onSplitterMoved(int pos, int index)
{
Q_UNUSED(pos);
Q_UNUSED(index);
auto s = sizes();
if (s.count() < 2) {
nhlog::ui()->warn("Splitter needs at least two children");
return;
}
if (s[0] == sz_.normal) {
rightMoveCount_ += 1;
if (rightMoveCount_ > moveEventLimit_) {
auto left = widget(0);
auto cursorPosition = left->mapFromGlobal(QCursor::pos());
// if we are coming from the right, the cursor should
// end up on the first widget.
if (left->rect().contains(cursorPosition)) {
left->setMinimumWidth(sz_.small);
left->setMaximumWidth(sz_.small);
rightMoveCount_ = 0;
}
}
} else if (s[0] == sz_.small) {
leftMoveCount_ += 1;
if (leftMoveCount_ > moveEventLimit_) {
auto left = widget(0);
auto right = widget(1);
auto cursorPosition = right->mapFromGlobal(QCursor::pos());
// We move the start a little further so the transition isn't so abrupt.
auto extended = right->rect();
extended.translate(100, 0);
// if we are coming from the left, the cursor should
// end up on the second widget.
if (extended.contains(cursorPosition) &&
right->size().width() >= sz_.collapsePoint + sz_.normal) {
left->setMinimumWidth(sz_.normal);
left->setMaximumWidth(2 * sz_.normal);
leftMoveCount_ = 0;
}
}
}
}
void
Splitter::hideSidebar()
{
auto left = widget(0);
if (left)
left->hide();
}
void
Splitter::showChatView()
{
auto left = widget(0);
auto right = widget(1);
if (right->isHidden()) {
left->hide();
right->show();
// Restore previous size.
if (left->minimumWidth() == sz_.small) {
left->setMinimumWidth(sz_.small);
left->setMaximumWidth(sz_.small);
} else {
left->setMinimumWidth(sz_.normal);
left->setMaximumWidth(2 * sz_.normal);
}
}
}
void
Splitter::showFullRoomList()
{
auto left = widget(0);
auto right = widget(1);
right->hide();
left->show();
left->setMaximumWidth(MaxWidth);
}
splitter::SideBarSizes
splitter::calculateSidebarSizes(const QFont &f)
{
const auto height = static_cast<double>(QFontMetrics{f}.lineSpacing());
SideBarSizes sz;
sz.small = std::ceil(3.8 * height);
sz.normal = std::ceil(16 * height);
sz.groups = std::ceil(3 * height);
sz.collapsePoint = 2 * sz.normal;
return sz;
}

View file

@ -1,49 +0,0 @@
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QSplitter>
namespace splitter {
struct SideBarSizes
{
int small;
int normal;
int groups;
int collapsePoint;
};
SideBarSizes
calculateSidebarSizes(const QFont &f);
}
class Splitter : public QSplitter
{
Q_OBJECT
public:
explicit Splitter(QWidget *parent = nullptr);
~Splitter() override;
void restoreSizes(int fallback);
public slots:
void hideSidebar();
void showFullRoomList();
void showChatView();
signals:
void hiddenSidebar();
private:
void onSplitterMoved(int pos, int index);
int moveEventLimit_ = 50;
int leftMoveCount_ = 0;
int rightMoveCount_ = 0;
splitter::SideBarSizes sz_;
};

View file

@ -1,219 +0,0 @@
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QInputDialog>
#include <QLabel>
#include <QMenu>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
#include <QTimer>
#include <iostream>
#include "ChatPage.h"
#include "Config.h"
#include "MainWindow.h"
#include "Splitter.h"
#include "UserInfoWidget.h"
#include "UserSettingsPage.h"
#include "ui/Avatar.h"
#include "ui/FlatButton.h"
#include "ui/OverlayModal.h"
UserInfoWidget::UserInfoWidget(QWidget *parent)
: QWidget(parent)
, display_name_("User")
, user_id_("@user:homeserver.org")
{
QFont f;
f.setPointSizeF(f.pointSizeF());
const int fontHeight = QFontMetrics(f).height();
const int widgetMargin = fontHeight / 3;
const int contentHeight = fontHeight * 3;
logoutButtonSize_ = std::min(fontHeight, 20);
setFixedHeight(contentHeight + widgetMargin);
topLayout_ = new QHBoxLayout(this);
topLayout_->setSpacing(0);
topLayout_->setMargin(widgetMargin);
avatarLayout_ = new QHBoxLayout();
textLayout_ = new QVBoxLayout();
textLayout_->setSpacing(widgetMargin / 2);
textLayout_->setContentsMargins(widgetMargin * 2, widgetMargin, widgetMargin, widgetMargin);
userAvatar_ = new Avatar(this, fontHeight * 2.5);
userAvatar_->setObjectName("userAvatar");
userAvatar_->setLetter(QChar('?'));
QFont nameFont;
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
nameFont.setWeight(QFont::Medium);
displayNameLabel_ = new QLabel(this);
displayNameLabel_->setFont(nameFont);
displayNameLabel_->setObjectName("displayNameLabel");
displayNameLabel_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
userIdLabel_ = new QLabel(this);
userIdLabel_->setFont(f);
userIdLabel_->setObjectName("userIdLabel");
userIdLabel_->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
avatarLayout_->addWidget(userAvatar_);
textLayout_->addWidget(displayNameLabel_, 0, Qt::AlignBottom);
textLayout_->addWidget(userIdLabel_, 0, Qt::AlignTop);
topLayout_->addLayout(avatarLayout_);
topLayout_->addLayout(textLayout_);
topLayout_->addStretch(1);
buttonLayout_ = new QHBoxLayout();
buttonLayout_->setSpacing(0);
buttonLayout_->setMargin(0);
logoutButton_ = new FlatButton(this);
logoutButton_->setToolTip(tr("Logout"));
logoutButton_->setCornerRadius(logoutButtonSize_ / 2);
QIcon icon;
icon.addFile(":/icons/icons/ui/power-button-off.png");
logoutButton_->setIcon(icon);
logoutButton_->setIconSize(QSize(logoutButtonSize_, logoutButtonSize_));
buttonLayout_->addWidget(logoutButton_);
topLayout_->addLayout(buttonLayout_);
// Show the confirmation dialog.
connect(logoutButton_, &QPushButton::clicked, this, []() {
MainWindow::instance()->openLogoutDialog();
});
menu = new QMenu(this);
auto setStatusAction = menu->addAction(tr("Set custom status message"));
connect(setStatusAction, &QAction::triggered, this, [this]() {
bool ok = false;
QString text = QInputDialog::getText(this,
tr("Custom status message"),
tr("Status:"),
QLineEdit::Normal,
ChatPage::instance()->status(),
&ok);
if (ok)
ChatPage::instance()->setStatus(text);
});
auto userProfileAction = menu->addAction(tr("User Profile Settings"));
connect(
userProfileAction, &QAction::triggered, this, [this]() { emit openGlobalUserProfile(); });
#if 0 // disable presence menu until issues in synapse are resolved
auto setAutoPresence = menu->addAction(tr("Set presence automatically"));
connect(setAutoPresence, &QAction::triggered, this, []() {
ChatPage::instance()->userSettings()->setPresence(
UserSettings::Presence::AutomaticPresence);
ChatPage::instance()->setStatus(ChatPage::instance()->status());
});
auto setOnline = menu->addAction(tr("Online"));
connect(setOnline, &QAction::triggered, this, []() {
ChatPage::instance()->userSettings()->setPresence(UserSettings::Presence::Online);
ChatPage::instance()->setStatus(ChatPage::instance()->status());
});
auto setUnavailable = menu->addAction(tr("Unavailable"));
connect(setUnavailable, &QAction::triggered, this, []() {
ChatPage::instance()->userSettings()->setPresence(
UserSettings::Presence::Unavailable);
ChatPage::instance()->setStatus(ChatPage::instance()->status());
});
auto setOffline = menu->addAction(tr("Offline"));
connect(setOffline, &QAction::triggered, this, []() {
ChatPage::instance()->userSettings()->setPresence(UserSettings::Presence::Offline);
ChatPage::instance()->setStatus(ChatPage::instance()->status());
});
#endif
}
void
UserInfoWidget::contextMenuEvent(QContextMenuEvent *event)
{
menu->popup(event->globalPos());
}
void
UserInfoWidget::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
const auto sz = splitter::calculateSidebarSizes(QFont{});
if (width() <= sz.small) {
topLayout_->setContentsMargins(0, 0, logoutButtonSize_, 0);
userAvatar_->hide();
displayNameLabel_->hide();
userIdLabel_->hide();
} else {
topLayout_->setMargin(5);
userAvatar_->show();
displayNameLabel_->show();
userIdLabel_->show();
}
QWidget::resizeEvent(event);
}
void
UserInfoWidget::reset()
{
displayNameLabel_->setText("");
userIdLabel_->setText("");
userAvatar_->setLetter(QChar('?'));
}
void
UserInfoWidget::setDisplayName(const QString &name)
{
if (name.isEmpty())
display_name_ = user_id_.split(':')[0].split('@')[1];
else
display_name_ = name;
displayNameLabel_->setText(display_name_);
userAvatar_->setLetter(QChar(display_name_[0]));
update();
}
void
UserInfoWidget::setUserId(const QString &userid)
{
user_id_ = userid;
userIdLabel_->setText(userid);
update();
}
void
UserInfoWidget::setAvatar(const QString &url)
{
userAvatar_->setImage(url);
update();
}
void
UserInfoWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View file

@ -1,68 +0,0 @@
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QWidget>
class Avatar;
class FlatButton;
class OverlayModal;
class QLabel;
class QHBoxLayout;
class QVBoxLayout;
class QMenu;
class UserInfoWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
public:
UserInfoWidget(QWidget *parent = nullptr);
void setDisplayName(const QString &name);
void setUserId(const QString &userid);
void setAvatar(const QString &url);
void reset();
QColor borderColor() const { return borderColor_; }
void setBorderColor(QColor &color) { borderColor_ = color; }
protected:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void contextMenuEvent(QContextMenuEvent *) override;
signals:
void openGlobalUserProfile();
private:
Avatar *userAvatar_;
QHBoxLayout *topLayout_;
QHBoxLayout *avatarLayout_;
QVBoxLayout *textLayout_;
QHBoxLayout *buttonLayout_;
FlatButton *logoutButton_;
QLabel *displayNameLabel_;
QLabel *userIdLabel_;
QString display_name_;
QString user_id_;
QImage avatar_image_;
int logoutButtonSize_;
QColor borderColor_;
QMenu *menu = nullptr;
};

View file

@ -10,6 +10,7 @@
#include "Cache_p.h"
#include "ChatPage.h"
#include "Logging.h"
#include "MainWindow.h"
#include "UserSettingsPage.h"
#include "Utils.h"
@ -113,3 +114,29 @@ Nheko::currentUser() const
return currentUser_.get();
}
void
Nheko::showUserSettingsPage() const
{
ChatPage::instance()->showUserSettingsPage();
}
void
Nheko::openLogoutDialog() const
{
MainWindow::instance()->openLogoutDialog();
}
void
Nheko::openCreateRoomDialog() const
{
MainWindow::instance()->openCreateRoomDialog(
[](const mtx::requests::CreateRoom &req) { ChatPage::instance()->createRoom(req); });
}
void
Nheko::openJoinRoomDialog() const
{
MainWindow::instance()->openJoinRoomDialog(
[](const QString &room_id) { ChatPage::instance()->joinRoom(room_id); });
}

View file

@ -40,6 +40,10 @@ public:
Q_INVOKABLE void openLink(QString link) const;
Q_INVOKABLE void setStatusMessage(QString msg) const;
Q_INVOKABLE void showUserSettingsPage() const;
Q_INVOKABLE void openLogoutDialog() const;
Q_INVOKABLE void openCreateRoomDialog() const;
Q_INVOKABLE void openJoinRoomDialog() const;
public slots:
void updateUserProfile();