Added a basic version of the chat window.

This commit is contained in:
Dimitar Dobrev 2014-03-07 16:41:13 +02:00
parent 1c2e752f3c
commit 437cadf360
8 changed files with 612 additions and 1 deletions

View file

@ -161,7 +161,7 @@ Menu::Menu() :
QMenu* toolsMenu = addMenu("Tools");
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat()));
QMenu* viewMenu = addMenu("View");
@ -1021,6 +1021,24 @@ void Menu::showMetavoxelEditor() {
_MetavoxelEditor->raise();
}
void Menu::showChat() {
if (!_chatWindow) {
_chatWindow = new ChatWindow();
QMainWindow* mainWindow = Application::getInstance()->getWindow();
// the height of the title bar is given by frameGeometry().height() - geometry().height()
// however, frameGeometry() is initialised after showing (Qt queries the OS windowing system)
// on the other hand, moving a window after showing it flickers; so just use some reasonable value
int titleBarHeight = 16;
_chatWindow->setGeometry(mainWindow->width() - _chatWindow->width(),
mainWindow->geometry().y() + titleBarHeight,
_chatWindow->width(),
mainWindow->height() - titleBarHeight);
_chatWindow->show();
}
_chatWindow->raise();
}
void Menu::audioMuteToggled() {
QAction *muteAction = _actionHash.value(MenuOption::MuteAudio);
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());

View file

@ -18,6 +18,8 @@
#include <MenuItemProperties.h>
#include <OctreeConstants.h>
#include <ui/ChatWindow.h>
const float ADJUST_LOD_DOWN_FPS = 40.0;
const float ADJUST_LOD_UP_FPS = 55.0;
@ -53,6 +55,7 @@ class QSettings;
class BandwidthDialog;
class LodToolsDialog;
class MetavoxelEditor;
class ChatWindow;
class OctreeStatsDialog;
class MenuItemProperties;
@ -140,6 +143,7 @@ private slots:
void cycleFrustumRenderMode();
void runTests();
void showMetavoxelEditor();
void showChat();
void audioMuteToggled();
private:
@ -187,6 +191,7 @@ private:
FrustumDrawMode _frustumDrawMode;
ViewFrustumOffset _viewFrustumOffset;
QPointer<MetavoxelEditor> _MetavoxelEditor;
QPointer<ChatWindow> _chatWindow;
OctreeStatsDialog* _octreeStatsDialog;
LodToolsDialog* _lodToolsDialog;
int _maxVoxels;
@ -252,6 +257,7 @@ namespace MenuOption {
const QString Logout = "Logout";
const QString LookAtVectors = "Look-at Vectors";
const QString MetavoxelEditor = "Metavoxel Editor...";
const QString Chat = "Chat...";
const QString Metavoxels = "Metavoxels";
const QString Mirror = "Mirror";
const QString MoveWithLean = "Move with Lean";

View file

@ -0,0 +1,129 @@
#include <QFormLayout>
#include <QFrame>
#include <QLayoutItem>
#include <QScrollBar>
#include <QSizePolicy>
#include <QTextDocument>
#include "ChatWindow.h"
#include "ui_chatwindow.h"
#include "FlowLayout.h"
#include <QXmppClient.h>
#include <QXmppMessage.h>
#include <Application.h>
#include <AccountManager.h>
const QString DEFAULT_SERVER = "chat.highfidelity.io";
const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io";
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
ChatWindow::ChatWindow() :
QDialog(Application::getInstance()->getGLWidget(), Qt::Tool),
ui(new Ui::ChatWindow) {
ui->setupUi(this);
FlowLayout* flowLayout = new FlowLayout();
flowLayout->setContentsMargins(0, 8, 0, 8);
ui->usersWidget->setLayout(flowLayout);
ui->messagePlainTextEdit->installEventFilter(this);
ui->numOnlineLabel->hide();
ui->usersWidget->hide();
ui->messagesScrollArea->hide();
ui->messagePlainTextEdit->hide();
setAttribute(Qt::WA_DeleteOnClose);
_xmppClient.addExtension(&_xmppMUCManager);
connect(&_xmppClient, SIGNAL(connected()), this, SLOT(connected()));
connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(error(QXmppClient::Error)));
connect(&_xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage)));
AccountManager& accountManager = AccountManager::getInstance();
QString user = accountManager.getUsername();
const QString& password = accountManager.getXMPPPassword();
_xmppClient.connectToServer(user + "@" + DEFAULT_SERVER, password);
}
ChatWindow::~ChatWindow() {
delete ui;
}
bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
Q_UNUSED(sender);
if (event->type() != QEvent::KeyPress) {
return false;
}
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) &&
(keyEvent->modifiers() & Qt::ShiftModifier) == 0) {
_xmppClient.sendMessage(_chatRoom->jid(), ui->messagePlainTextEdit->document()->toPlainText());
ui->messagePlainTextEdit->document()->clear();
return true;
}
return false;
}
QString ChatWindow::getParticipantName(const QString& participant) {
return participant.right(participant.count() - 1 - _chatRoom->jid().count());
}
void ChatWindow::connected() {
_chatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
connect(_chatRoom, SIGNAL(participantsChanged()), this, SLOT(participantsChanged()));
_chatRoom->setNickName(AccountManager::getInstance().getUsername());
_chatRoom->join();
ui->connectingToXMPPLabel->hide();
ui->numOnlineLabel->show();
ui->usersWidget->show();
ui->messagesScrollArea->show();
ui->messagePlainTextEdit->show();
}
void ChatWindow::error(QXmppClient::Error error) {
ui->connectingToXMPPLabel->setText(QString::number(error));
}
void ChatWindow::participantsChanged() {
QStringList participants = _chatRoom->participants();
ui->numOnlineLabel->setText(tr("%1 online now:").arg(participants.count()));
while (QLayoutItem* item = ui->usersWidget->layout()->takeAt(0)) {
delete item;
}
foreach (const QString& participant, participants) {
QLabel* userLabel = new QLabel(getParticipantName(participant));
userLabel->setStyleSheet(
"background-color: palette(light);"
"border-radius: 5px;"
"color: #267077;"
"padding: 2px;"
"border: 1px solid palette(shadow);"
"font-weight: bold");
ui->usersWidget->layout()->addWidget(userLabel);
}
}
void ChatWindow::messageReceived(const QXmppMessage& message) {
QLabel* userLabel = new QLabel(getParticipantName(message.from()));
QFont font = userLabel->font();
font.setBold(true);
userLabel->setFont(font);
userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "<a href=\"\\1\">\\1</a>"));
messageLabel->setWordWrap(true);
messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
messageLabel->setOpenExternalLinks(true);
ui->messagesFormLayout->addRow(userLabel, messageLabel);
ui->messagesFormLayout->parentWidget()->updateGeometry();
Application::processEvents();
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
verticalScrollBar->setSliderPosition(verticalScrollBar->maximum());
}

View file

@ -0,0 +1,40 @@
#ifndef __interface__ChatWindow__
#define __interface__ChatWindow__
#include <QDialog>
#include <Application.h>
#include <QXmppClient.h>
#include <QXmppMucManager.h>
namespace Ui {
class ChatWindow;
}
class ChatWindow : public QDialog {
Q_OBJECT
public:
ChatWindow();
~ChatWindow();
protected:
bool eventFilter(QObject* sender, QEvent* event);
private:
Ui::ChatWindow* ui;
QXmppClient _xmppClient;
QXmppMucManager _xmppMUCManager;
QXmppMucRoom* _chatRoom;
QString getParticipantName(const QString& participant);
private slots:
void connected();
void error(QXmppClient::Error error);
void participantsChanged();
void messageReceived(const QXmppMessage& message);
};
#endif /* defined(__interface__ChatWindow__) */

View file

@ -0,0 +1,213 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtWidgets>
#include "flowlayout.h"
//! [1]
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
: m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
//! [1]
//! [2]
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
//! [2]
//! [3]
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
//! [3]
//! [4]
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0) {
return m_hSpace;
} else {
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0) {
return m_vSpace;
} else {
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
}
//! [4]
//! [5]
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem *FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return 0;
}
//! [5]
//! [6]
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
//! [6]
//! [7]
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
//! [7]
//! [8]
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
//! [8]
//! [9]
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
//! [9]
//! [10]
QLayoutItem *item;
foreach (item, itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
//! [10]
//! [11]
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom;
}
//! [11]
//! [12]
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parent = this->parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
QWidget *pw = static_cast<QWidget *>(parent);
return pw->style()->pixelMetric(pm, 0, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
}
//! [12]

View file

@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
//! [0]
class FlowLayout : public QLayout
{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout();
void addItem(QLayoutItem *item);
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const;
bool hasHeightForWidth() const;
int heightForWidth(int) const;
int count() const;
QLayoutItem *itemAt(int index) const;
QSize minimumSize() const;
void setGeometry(const QRect &rect);
QSize sizeHint() const;
QLayoutItem *takeAt(int index);
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList<QLayoutItem *> itemList;
int m_hSpace;
int m_vSpace;
};
//! [0]
#endif // FLOWLAYOUT_H

125
interface/ui/chatWindow.ui Normal file
View file

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChatWindow</class>
<widget class="QDialog" name="ChatWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>608</height>
</rect>
</property>
<property name="windowTitle">
<string>Chat</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="connectingToXMPPLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Connecting to XMPP...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="numOnlineLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-weight: bold; color: palette(shadow)</string>
</property>
<property name="text">
<string> online now:</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="usersWidget" native="true"/>
</item>
<item>
<widget class="QScrollArea" name="messagesScrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<height>356</height>
</rect>
</property>
<layout class="QFormLayout" name="messagesFormLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>4</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>messagePlainTextEdit</tabstop>
<tabstop>messagesScrollArea</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -52,6 +52,8 @@ public:
void requestAccessToken(const QString& login, const QString& password);
QString getUsername() const { return _accountInfo.getUsername(); }
const QString& getXMPPPassword() const { return _accountInfo.getXMPPPassword(); }
void destroy() { delete _networkAccessManager; }